diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fca96d0..d7abce1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,26 +21,39 @@ jobs: pip install -U pip pip install --upgrade coveralls setuptools setuptools_scm pep517 .[tests] pip install . - - name: Mypy testing (<3.11) + + - name: Mypy testing (<3.8) run: | + echo ${{ matrix.python-version }} pip install mypy==0.910 - python -m mypy executing --exclude=executing/_position_node_finder.py + python -m mypy executing --exclude 'executing/_.*_node_finder.py' # fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3 - if: ${{ !contains(fromJson('["2.7", "pypy2", "pypy-3.6", "3.11-dev"]'), matrix.python-version) }} + if: ${{ contains(fromJson('["3.5","3.6","3.7"]'), matrix.python-version) }} # pypy < 3.8 very doesn't work # 2.7 is tested separately in mypy-py2, as we need to run mypy under Python 3.x + + - name: Mypy testing (<3.11) + run: | + pip install mypy==0.910 + python -m mypy executing --exclude=executing/_position_node_finder.py + # fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3 + if: ${{ contains(fromJson('["3.8","3.9","3.10"]'), matrix.python-version) }} + - name: Mypy testing (3.11) run: | pip install mypy==0.971 python -m mypy executing # fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3 - if: ${{ contains(fromJson('["3.11-dev"]'), matrix.python-version) }} + if: ${{ contains(fromJson('["3.11.1"]'), matrix.python-version) }} # only >=3.11 use _position_node_finder.py + - name: Test env: EXECUTING_SLOW_TESTS: 1 run: | - coverage run --include=executing/executing.py --append -m pytest tests + export COVERAGE_PROCESS_START=${GITHUB_WORKSPACE}/setup.cfg + coverage run -m pytest tests + coverage combine coverage report -m - name: Coveralls Python uses: AndreMiras/coveralls-python-action@v20201129 @@ -75,4 +88,4 @@ jobs: pip install mypy[python2]==0.910 - name: Mypy testing for Python 2 run: | - python -m mypy --py2 executing --exclude=executing/_position_node_finder.py + python -m mypy --py2 executing --exclude 'executing/_.*_node_finder.py' diff --git a/Features.md b/Features.md new file mode 100644 index 0000000..b0c159c --- /dev/null +++ b/Features.md @@ -0,0 +1,221 @@ +# Features +## Overview +The following table lists the supported places where executing can be used to map to a ast-node. +| name | 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|------------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------| +| binary operators | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `a(arguments...)` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| compare ops |(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)| +| `del obj.attr` |(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)| :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |(:white_check_mark:)|(:white_check_mark:)|(:white_check_mark:)| +| `del obj[index]` | :x: | :x: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | +| format string | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `obj.attr` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `obj[index]` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +|inplace binary operators| :x: | :x: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | +| known issues | :heavy_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +|reverse binary operators| :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `obj.attr = 5` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `obj[index]=value` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| `with contextmanager:` | :x: | :x: | :x: | :x: |(:white_check_mark:)|(:white_check_mark:)| :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | :x: | +## Details + +### binary operators + +the node of an binary operation can be obtained inside a `__add__` or other binary operator. + + + +|name| 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|----|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------| +| `+`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `-`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `/`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`//`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `*`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `%`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`**`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`<<`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`>>`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`\|`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `&`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `^`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `@`| |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| |:heavy_check_mark:|:heavy_check_mark:| + +### `a(arguments...)` + +the node of an binary operation can be obtained inside function or `__call__` operator. + + + +| name | 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------| +|`call`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| + +### compare ops + +map compare ops: + +* `t<5`: `__lt__` resolves to `ast.Compare(ops=[ast.Lt], ...)` +* `5` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `>=` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `!=` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `==` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `in` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `not in` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `assert 5>=`|:x:|:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | +|`\|=`|:x:|:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | +| `&=`|:x:|:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | +| `^=`|:x:|:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | +| `@=`| |:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| | :x: | :x: | + +### known issues + +some known issues + + + +| name | 2.7 |3.5|3.6|3.7|3.8|3.9|3.10| 3.11 | pypy2.7 |pypy3.5|pypy3.6| +|----------------|------------------|---|---|---|---|---|----|------------------|------------------|-------|-------| +|`same generator`|:heavy_check_mark:|:x:|:x:|:x:|:x:|:x:| :x:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | + +### reverse binary operators + +the node of an binary operation can be obtained inside a `__radd__` or other binary operator. + + + +|name| 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|----|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------| +| `+`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `-`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `/`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`//`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `*`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `%`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`**`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`<<`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`>>`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`\|`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `&`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `^`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `@`| |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| |:heavy_check_mark:|:heavy_check_mark:| + +### `obj.attr = 5` + +the node can be accessed inside the `__setattr__` function. + + + +| name | 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|----------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------| +| `t.attr = 5` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`t.attr, g.attr= 5, 5`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +| `setattr(t,"attr",5)`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| + +### `obj[index]=value` + +the node can be accessed inside the `__setitem__` function. + + + +| name | 2.7 | 3.5 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 | pypy2.7 | pypy3.5 | pypy3.6 | +|----------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------| +| `obj[index]=value` |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| +|`obj[start:end]=value`|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| + +### `with contextmanager:` + +__enter__ and __exit__ + + + +| name |2.7|3.5|3.6|3.7| 3.8 | 3.9 | 3.10 | 3.11 |pypy2.7|pypy3.5|pypy3.6| +|-----------|---|---|---|---|------------------|------------------|------------------|------------------|-------|-------|-------| +|`__enter__`|:x:|:x:|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | +| `__exit__`|:x:|:x:|:x:|:x:| :x: | :x: |:heavy_check_mark:|:heavy_check_mark:| :x: | :x: | :x: | \ No newline at end of file diff --git a/executing/_base_node_finder.py b/executing/_base_node_finder.py new file mode 100644 index 0000000..105ac88 --- /dev/null +++ b/executing/_base_node_finder.py @@ -0,0 +1,553 @@ + +import ast +import sys +import dis +from types import FrameType +from typing import Any, Callable, Iterator, Optional, Sequence, Set, Tuple, Type, Union, cast,List +from .executing import EnhancedAST, NotOneValueFound, Source, only, function_node_types, assert_ +from ._exceptions import KnownIssue, VerifierFailure + +try: + from types import CodeType +except ImportError: + CodeType=type((lambda:None).__code__) # type: ignore[misc] + +from functools import lru_cache + +# the code in this module can use all python>=3.11 features + +py11 = sys.version_info >= (3, 11) + +from ._helper import node_and_parents, parents, before + + +def mangled_name(node: Union[EnhancedAST,ast.ExceptHandler]) -> str: + """ + + Parameters: + node: the node which should be mangled + name: the name of the node + + Returns: + The mangled name of `node` + """ + name:str + if isinstance(node, ast.Attribute): + name = node.attr + elif isinstance(node, ast.Name): + name = node.id + elif isinstance(node, (ast.alias)): + name = node.asname or (node.name or "").split(".")[0] + elif isinstance(node, ast.AugAssign) and isinstance(node.target,ast.Name): + name = node.target.id + elif isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): + name = node.name + elif sys.version_info >= (3,10) and isinstance(node, (ast.MatchStar, ast.MatchAs)): + name = node.name or "" + elif isinstance(node, ast.ExceptHandler): + assert node.name + name = node.name + else: + raise TypeError("no node to mangle") + + if name.startswith("__") and not name.endswith("__"): + parent:EnhancedAST + child:EnhancedAST + + parent,child=cast(EnhancedAST, node).parent,cast(EnhancedAST,node) + + while not (isinstance(parent,ast.ClassDef) and child not in parent.bases): + if not hasattr(parent,"parent"): + break # pragma: no mutate + + parent,child=parent.parent,parent + else: + class_name=parent.name.lstrip("_") + if class_name!="": + return "_" + class_name + name + + + + return name + + +@lru_cache(128) # pragma: no mutate +def get_instructions(code: CodeType) -> List[dis.Instruction]: + return list(dis.get_instructions(code, show_caches=True)) # type: ignore[call-arg] + + +op_type_map_py11 = { + "**": ast.Pow, + "*": ast.Mult, + "@": ast.MatMult, + "//": ast.FloorDiv, + "/": ast.Div, + "%": ast.Mod, + "+": ast.Add, + "-": ast.Sub, + "<<": ast.LShift, + ">>": ast.RShift, + "&": ast.BitAnd, + "^": ast.BitXor, + "|": ast.BitOr, +} + +op_type_map = { + "POWER": ast.Pow, + "MULTIPLY": ast.Mult, + "MATRIX_MULTIPLY": ast.MatMult, + "FLOOR_DIVIDE": ast.FloorDiv, + "TRUE_DIVIDE": ast.Div, + "MODULO": ast.Mod, + "ADD": ast.Add, + "SUBTRACT": ast.Sub, + "LSHIFT": ast.LShift, + "RSHIFT": ast.RShift, + "AND": ast.BitAnd, + "XOR": ast.BitXor, + "OR": ast.BitOr, +} + + + + +class BaseNodeFinder(object): + + + @staticmethod + def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool: + if inst.opname not in ( + "STORE_NAME", + "STORE_FAST", + "STORE_DEREF", + "STORE_GLOBAL", + "DELETE_NAME", + "DELETE_FAST", + "DELETE_DEREF", + "DELETE_GLOBAL", + ): + return False + + # This bytecode does something exception cleanup related. + # The position of the instruciton seems to be something in the last ast-node of the ExceptHandler + # this could be a bug, but it might not be observable in normal python code. + + # example: + # except Exception as exc: + # enum_member._value_ = value + + # other example: + # STORE_FAST of e was mapped to Constant(value=False) + # except OSError as e: + # if not _ignore_error(e): + # raise + # return False + + # STORE_FAST of msg was mapped to print(...) + # except TypeError as msg: + # print("Sorry:", msg, file=file) + + if ( + isinstance(node, ast.Name) + and isinstance(node.ctx,ast.Store) + and inst.opname.startswith("STORE_") + and mangled_name(node) == inst.argval + ): + # Storing the variable is valid and no exception cleanup, if the name is correct + return False + + if ( + isinstance(node, ast.Name) + and isinstance(node.ctx,ast.Del) + and inst.opname.startswith("DELETE_") + and mangled_name(node) == inst.argval + ): + # Deleting the variable is valid and no exception cleanup, if the name is correct + return False + + if not py11: + tmp = node + if isinstance(tmp, ast.Name): + tmp = tmp.parent + + if isinstance(tmp, ast.stmt): + for n in before(tmp): + for child in ast.walk(n): + if ( + isinstance(child, ast.ExceptHandler) + and child.name + and mangled_name(child) == inst.argval + ): + return True + + for child in ast.walk(node): + if ( + isinstance(child, ast.ExceptHandler) + and child.name + and mangled_name(child) == inst.argval + ): + return True + + if any( + handler.name and mangled_name(handler) == inst.argval + for parent in parents(node) + if isinstance(parent, ast.Try) + for handler in ast.walk(parent) + if isinstance(handler,ast.ExceptHandler) + ): + return True + + return any( + isinstance(n, ast.ExceptHandler) and n.name and mangled_name(n) == inst.argval + for n in parents(node) + ) + + def verify(self, node: EnhancedAST, instruction: dis.Instruction) -> None: + """ + checks if this node could gererate this instruction + """ + + op_name = instruction.opname + extra_filter: Callable[[EnhancedAST], bool] = lambda e: True + ctx: Type = type(None) + + def inst_match(opnames: Union[str, Sequence[str]], **kwargs: Any) -> bool: + """ + match instruction + + Parameters: + opnames: (str|Seq[str]): inst.opname has to be equal to or in `opname` + **kwargs: every arg has to match inst.arg + + Returns: + True if all conditions match the instruction + + """ + + if isinstance(opnames, str): + opnames = [opnames] + return instruction.opname in opnames and kwargs == { + k: getattr(instruction, k) for k in kwargs + } + + def node_match(node_type: Union[Type, Tuple[Type, ...]], **kwargs: Any) -> bool: + """ + match the ast-node + + Parameters: + node_type: type of the node + **kwargs: every `arg` has to be equal `node.arg` + or `node.arg` has to be an instance of `arg` if it is a type. + """ + return isinstance(node, node_type) and all( + isinstance(getattr(node, k), v) + if isinstance(v, type) + else getattr(node, k) == v + for k, v in kwargs.items() + ) + + if op_name == "CACHE": + return + + call_opname: Tuple[str, ...] + if py11: + call_opname = ("CALL",) + else: + call_opname = ("CALL_FUNCTION","SETUP_WITH") + + if inst_match("RETURN_VALUE") and node_match(ast.Return): + return + + if inst_match("LOAD_CONST") and node_match(ast.Constant): + return + + if inst_match(call_opname) and node_match((ast.With, ast.AsyncWith)): + # call to context.__exit__ + return + + if inst_match((*call_opname, "LOAD_FAST")) and node_match( + (ast.ListComp, ast.GeneratorExp, ast.SetComp, ast.DictComp) + ): + # call to the generator function + return + + if py11: + if inst_match(("CALL", "CALL_FUNCTION_EX")) and node_match( + (ast.ClassDef, ast.Call) + ): + return + else: + if inst_match( + ("CALL", "CALL_FUNCTION_EX") + + ("CALL_FUNCTION_KW", "CALL_FUNCTION", "CALL_METHOD") + ) and node_match((ast.ClassDef, ast.Call)): + return + + if inst_match( + ( + "CALL_FUNCTION_EX", + "CALL_FUNCTION_KW", + "CALL_FUNCTION", + "CALL_METHOD", + "LOAD_NAME", + ) + ) and node_match((ast.ClassDef)): + return + + if ( + inst_match("STORE_NAME") + and instruction.argval in ("__module__", "__qualname__") + and node_match((ast.ClassDef)) + ): + return + + if inst_match(("COMPARE_OP", "IS_OP", "CONTAINS_OP")) and node_match( + ast.Compare + ): + return + + if inst_match("LOAD_NAME", argval="__annotations__") and node_match( + ast.AnnAssign + ): + return + + if ( + ( + inst_match("LOAD_METHOD", argval="join") + or inst_match(("CALL", "BUILD_STRING")) + ) + and node_match(ast.BinOp, left=ast.Constant, op=ast.Mod) + and isinstance(cast(ast.Constant, cast(ast.BinOp, node).left).value, str) + ): + # "..."%(...) uses "".join + return + + if (sys.version_info[:2]==(3,10) and( + inst_match(("LOAD_METHOD"), argval="join") + or inst_match("CALL_METHOD") + # or inst_match(("CALL", "BUILD_STRING")) + ) + and node_match(ast.JoinedStr) + ): + # large format strings + return + + + if inst_match("STORE_SUBSCR") and node_match(ast.AnnAssign): + # data: int + return + + + if inst_match(("DELETE_NAME", "DELETE_FAST")) and node_match( + ast.Name, id=instruction.argval, ctx=ast.Del + ): + return + + if inst_match("BUILD_STRING") and ( + node_match(ast.JoinedStr) or node_match(ast.BinOp, op=ast.Mod) + ): + return + + if inst_match(("BEFORE_WITH","WITH_EXCEPT_START")) and node_match(ast.With): + return + + if inst_match(("STORE_NAME", "STORE_GLOBAL"), argval="__doc__") and node_match( + ast.Constant if py11 else (ast.Constant, ast.Expr) + ): + # store docstrings + return + + if ( + inst_match(("STORE_NAME", "STORE_FAST", "STORE_GLOBAL", "STORE_DEREF")) + and node_match(ast.ExceptHandler) + and instruction.argval == mangled_name(node) + ): + # store exception in variable + return + + if inst_match("COMPARE_OP",argrepr="exception match") and node_match(ast.ExceptHandler): + return + + if ( + inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL")) + and node_match((ast.Import, ast.ImportFrom)) + and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names) + ): + # store imported module in variable + return + + if ( + inst_match(("STORE_FAST", "STORE_DEREF", "STORE_NAME", "STORE_GLOBAL")) + and ( + node_match((ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)) + or node_match( + ast.Name, + ctx=ast.Store, + ) + ) + and instruction.argval == mangled_name(node) + ): + return + + if sys.version_info >= (3,10) and not py11: + # TODO: match expressions are not supported for now + if inst_match(("STORE_FAST", "STORE_NAME")) and node_match( + ast.MatchAs, name=instruction.argval + ): + return + + if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchSequence): + return + + if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchValue): + return + + if ( + inst_match(("STORE_FAST", "STORE_NAME", "STORE_GLOBAL")) + and isinstance(node.parent, ast.MatchAs) + and mangled_name(node.parent) == instruction.argval + ): + # TODO: bug? this should be map to the MatchAs directly + return + + if ( + inst_match(("STORE_FAST", "STORE_NAME", "STORE_GLOBAL")) + and isinstance(node, ast.MatchStar) + and mangled_name(node) == instruction.argval + ): + return + + if inst_match("COMPARE_OP", argval=">=") and isinstance( + node, ast.MatchSequence + ): + return + + if sys.version_info >= (3,11) and inst_match("BINARY_OP") and node_match( + ast.AugAssign, op=op_type_map_py11[instruction.argrepr.removesuffix("=")] + ): + # a+=5 + return + + if node_match(ast.Attribute, ctx=ast.Del) and inst_match( + "DELETE_ATTR", argval=mangled_name(node) + ): + return + + if inst_match(("JUMP_IF_TRUE_OR_POP", "JUMP_IF_FALSE_OR_POP")) and node_match( + ast.BoolOp + ): + # and/or short circuit + return + + if inst_match("DELETE_SUBSCR") and node_match(ast.Subscript, ctx=ast.Del): + return + + if ( + node_match(ast.Name, ctx=ast.Load) + or ( + node_match(ast.Name, ctx=ast.Store) + and isinstance(node.parent, ast.AugAssign) + ) + ) and inst_match( + ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL", "LOAD_DEREF"), argval=mangled_name(node) + ): + return + + if (node_match(ast.AugAssign)) and inst_match( + ( + "LOAD_NAME", + "LOAD_FAST", + "LOAD_GLOBAL", + "LOAD_DEREF", + "LOAD_CLASSDEREF", + "STORE_NAME", + "STORE_FAST", + "STORE_GLOBAL", + "STORE_DEREF", + ), + argval=mangled_name(node), + ): + return + + if node_match(ast.Name, ctx=ast.Del) and inst_match( + ("DELETE_NAME", "DELETE_GLOBAL", "DELETE_DEREF"), argval=mangled_name(node) + ): + return + + # old verifier + + typ: Type = type(None) + op_type: Type = type(None) + + if op_name.startswith(("BINARY_SUBSCR", "SLICE+")): + typ = ast.Subscript + ctx = ast.Load + elif op_name.startswith("BINARY_"): + typ = ast.BinOp + if py11: + op_type = op_type_map_py11[instruction.argrepr] + else: + op_type = op_type_map[op_name[7:]] + extra_filter = lambda e: isinstance(cast(ast.BinOp, e).op, op_type) + elif op_name.startswith("INPLACE_"): + typ = ast.AugAssign + assert not py11 + op_type = op_type_map[op_name[8:]] + extra_filter = lambda e: isinstance(cast(ast.BinOp, e).op, op_type) + + elif op_name.startswith("UNARY_"): + typ = ast.UnaryOp + op_type = dict( + UNARY_POSITIVE=ast.UAdd, + UNARY_NEGATIVE=ast.USub, + UNARY_NOT=ast.Not, + UNARY_INVERT=ast.Invert, + )[op_name] + extra_filter = lambda e: isinstance(cast(ast.UnaryOp, e).op, op_type) + elif op_name in ("LOAD_ATTR", "LOAD_METHOD", "LOOKUP_METHOD"): + typ = ast.Attribute + ctx = ast.Load + extra_filter = lambda e: mangled_name(e) == instruction.argval + elif op_name in ( + "LOAD_NAME", + "LOAD_GLOBAL", + "LOAD_FAST", + "LOAD_DEREF", + "LOAD_CLASSDEREF", + ): + typ = ast.Name + ctx = ast.Load + extra_filter = lambda e: cast(ast.Name, e).id == instruction.argval + elif op_name in ("COMPARE_OP", "IS_OP", "CONTAINS_OP"): + typ = ast.Compare + extra_filter = lambda e: len(cast(ast.Compare, e).ops) == 1 + elif op_name.startswith(("STORE_SLICE", "STORE_SUBSCR")): + ctx = ast.Store + typ = ast.Subscript + elif op_name.startswith("STORE_ATTR"): + ctx = ast.Store + typ = ast.Attribute + extra_filter = lambda e: mangled_name(e) == instruction.argval + + node_ctx = getattr(node, "ctx", None) + + ctx_match = ( + ctx is not type(None) + or not hasattr(node, "ctx") + or isinstance(node_ctx, ctx) + ) + + # check for old verifier + if isinstance(node, typ) and ctx_match and extra_filter(node): + return + + # generate error + + title = "ast.%s is not created from %s" % ( + type(node).__name__, + instruction.opname, + ) + + raise VerifierFailure(title, node, instruction) + + diff --git a/executing/_exceptions.py b/executing/_exceptions.py index 1e88e3f..47db8e8 100644 --- a/executing/_exceptions.py +++ b/executing/_exceptions.py @@ -20,3 +20,13 @@ def __init__(self, title, node, instruction): self.instruction = instruction super().__init__(title) # type: ignore[call-arg] + + +class MultipleMatches(Exception): + def __init__(self,nodes): + # type: (object) -> None + self.nodes=nodes + + +class NoMatch(Exception): + pass diff --git a/executing/_helper.py b/executing/_helper.py new file mode 100644 index 0000000..54930a5 --- /dev/null +++ b/executing/_helper.py @@ -0,0 +1,33 @@ +from .executing import EnhancedAST, NotOneValueFound, Source, only, function_node_types, assert_ +from typing import Any, Callable, Iterator, Optional, Sequence, Set, Tuple, Type, Union, cast +import ast +import sys + +py11 = sys.version_info >= (3, 11) # type: bool + +def parents(node): + # type: (EnhancedAST) -> Iterator[EnhancedAST] + while hasattr(node, "parent"): + node = node.parent + yield node + + +def has_parent(node, node_type): + # type: (EnhancedAST,Union[Type,Tuple[Type,...]]) -> bool + return any(isinstance(p, node_type) for p in parents(node)) + + +def node_and_parents(node) : + # type: (EnhancedAST) -> Iterator[EnhancedAST] + yield node + for n in parents(node): + yield n + +def before(node): + # type: (EnhancedAST) -> Iterator[EnhancedAST] + parent_list = only( + l for _, l in ast.iter_fields(node.parent) if isinstance(l, list) and node in l + ) + index = parent_list.index(node) + return reversed(parent_list[:index]) + diff --git a/executing/_index_node_finder.py b/executing/_index_node_finder.py new file mode 100644 index 0000000..2a78968 --- /dev/null +++ b/executing/_index_node_finder.py @@ -0,0 +1,740 @@ +from __future__ import annotations # type: ignore[attr-defined] + +import sys + +assert ( + (3, 8) <= sys.version_info < (3, 11) +), "This NodeFinder can only be used for python 3.8, 3.9 and 3.10" + + +import copy +import ast +import dis +import math +from types import FrameType +from typing import ( + Sequence, + Iterable, + Set, + Callable, + Tuple, + List, + Optional, + Iterator, + Dict, + TypeVar, + Union, + Any, + cast, +) +from dataclasses import dataclass + + +from .executing import ( + EnhancedAST, + NotOneValueFound, + Source, + function_node_types, + assert_, + TESTING, +) + +from ._exceptions import KnownIssue, MultipleMatches, NoMatch + +from functools import lru_cache +from collections import defaultdict, namedtuple +from types import CodeType + +from ._base_node_finder import BaseNodeFinder +from ._helper import node_and_parents, has_parent + + +# the main reason for this rewrite hooks is pytest, +# because we want to support the rewritten asserts in tests +pytest_rewrite_assert: Any + +try: + from _pytest.assertion.rewrite import rewrite_asserts as pytest_rewrite_assert +except: # pragma: no cover + pytest_rewrite_assert = None + + +Match = namedtuple("Match", "nodes code index_code") + + +def all_equal(seq: Iterator[Any]) -> bool: + s, *rest = seq + return all(s == e for e in rest) + + +@dataclass +class InstFingerprint: + opcode: int + value: Any + line: Optional[int] + offset: int + index: int + next: Tuple[int, ...] + + @property + def opname(self) -> str: + # only used for debugging + return dis.opname[self.opcode] # pragma: no cover + +def normalize_opcode(opcode:int) -> int: + if opcode==opcodes.PRINT_EXPR: + # PRINT_EXPR is used for expression evaluation in the (i)python-repl + return opcodes.POP_TOP + return opcode + +def normalize(value: Any) -> Any: + """normalize nan + + normalize nan to a random float because nan!=nan + This would otherwise be a problem for the key lookup in CodeMap + """ + if isinstance(value, float) and math.isnan(value): + return 0.424420436760876254610289124609191284195 + elif isinstance(value, tuple): + return tuple(normalize(v) for v in value) + elif isinstance(value, complex): + return complex(normalize(value.real), normalize(value.imag)) + elif isinstance(value, CodeType): + return tuple( + (normalize_opcode(inst.opcode), inst.offset, normalize(inst.argval)) + for inst in dis.get_instructions(value) + ) + return value + + +class OpCodes: + def __getattr__(self, name: str) -> int: + # getattr makes mypy happy + assert False, "%s is not a valid opcode" % name + + +for key, value in dis.opmap.items(): + setattr(OpCodes, key, value) + +opcodes = OpCodes() + +hasjcond = [ + opcodes.POP_JUMP_IF_TRUE, + opcodes.POP_JUMP_IF_FALSE, + opcodes.JUMP_IF_TRUE_OR_POP, + opcodes.JUMP_IF_FALSE_OR_POP, +] + +end_of_block = [opcodes.RETURN_VALUE, opcodes.RAISE_VARARGS] + +skip_opcodes = (opcodes.NOP, opcodes.EXTENDED_ARG) + + +if sys.version_info >= (3, 9): + hasjcond += [opcodes.JUMP_IF_NOT_EXC_MATCH] + end_of_block += [opcodes.RERAISE] + + +class CodeFingerprint: + def __init__( + self, code: CodeType, lineno_map: Callable[[int], Optional[int]] = lambda a: a + ): + self.graph: Dict[int, InstFingerprint] = {} + + self.instructions: List[dis.Instruction] = [] + + last_lineno = None + for inst in dis.get_instructions(code): + if inst.starts_line: + last_lineno = inst.starts_line + self.instructions.append(inst._replace(starts_line=last_lineno)) + + assert all(i.offset == j * 2 for j, i in enumerate(self.instructions)) + + def get_next(n: int) -> List[Tuple[int, Any]]: + i = current_offset // 2 + return [(inst.opcode, inst.argval) for inst in self.instructions[i : i + n]] + + self.start = self.skip(0) + + todo = [self.start] if self.start is not None else [] + + while todo: + current_offset = todo.pop() + next_offset = current_offset + 2 + inst = self.instructions[current_offset // 2] + + opcode = inst.opcode + value = inst.argval + + optimize_result = self.optimize_tuple(inst) + + if optimize_result is not None: + opcode, value, next_offset = optimize_result + + if get_next(2) == [(opcodes.BUILD_TUPLE, 2), (opcodes.UNPACK_SEQUENCE, 2)]: + # normalize tuple optimization + opcode = opcodes.ROT_TWO + value = None + next_offset += 2 + + next_offsets: List[int] = [] + + def add_offset(offset: Union[int, None]) -> None: + if offset is not None: + next_offsets.append(offset) + + add_offset(self.skip(next_offset)) + + if opcode in hasjcond: + # this is also optimized by the python compiler if both instructions are on the same line + # see https://github.com/python/cpython/issues/100378#issuecomment-1360381947 + next_offset = inst.argval + if inst.opcode in [ + opcodes.JUMP_IF_TRUE_OR_POP, + opcodes.JUMP_IF_FALSE_OR_POP, + ]: + opcode, next_offset = self.optimize_jump(inst) + + add_offset(self.skip(next_offset)) + + if inst.opcode in ( + opcodes.SETUP_FINALLY, + opcodes.SETUP_WITH, + opcodes.SETUP_ASYNC_WITH, + opcodes.FOR_ITER, + ): + add_offset(self.skip(inst.argval)) + + if inst.opcode in end_of_block: + next_offsets = [] + + for offset in next_offsets: + if offset not in self.graph: + todo.append(offset) + + self.graph[current_offset] = InstFingerprint( + opcode, + value, + line=lineno_map(cast(int, inst.starts_line)), + offset=inst.offset, + index=cast(int, inst.starts_line), + next=tuple(next_offsets), + ) + + def next_opcode(self, offset: int) -> Optional[dis.Instruction]: + done = set() + while offset // 2 < len(self.instructions) and offset not in done: + done.add(offset) + inst = self.instructions[offset // 2] + if inst.opcode in skip_opcodes: + offset += 2 + elif inst.opcode in (opcodes.JUMP_FORWARD, opcodes.JUMP_ABSOLUTE): + offset = inst.argval + else: + return inst + return None + + def skip(self, offset: int) -> Optional[int]: + inst = self.next_opcode(offset) + if inst is not None: + return inst.offset + return None + + def follow_jump(self, offset: int) -> dis.Instruction: + inst = self.next_opcode(offset) + + # follow jump is used in optimize_jump, which is only used for instructions like JUMP_IF_X_OR_POP + # JUMP_IF_X_OR_POP is only generated for code like: + # a = b or c + # there can never be a loop and therefor next_opcode can never return None + assert inst is not None + + return inst + + def optimize_jump(self, inst: dis.Instruction) -> Tuple[int, int]: + pop = False + if inst.opcode == opcodes.JUMP_IF_FALSE_OR_POP: + # TOS=False + jump_or_pop = opcodes.JUMP_IF_FALSE_OR_POP + pop_and_no_jump = (opcodes.JUMP_IF_TRUE_OR_POP, opcodes.POP_JUMP_IF_TRUE) + + pop_and_jump = opcodes.POP_JUMP_IF_FALSE + + elif inst.opcode == opcodes.JUMP_IF_TRUE_OR_POP: + # TOS=True + jump_or_pop = opcodes.JUMP_IF_TRUE_OR_POP + pop_and_no_jump = (opcodes.JUMP_IF_FALSE_OR_POP, opcodes.POP_JUMP_IF_FALSE) + + pop_and_jump = opcodes.POP_JUMP_IF_TRUE + + else: + assert False, inst + + while inst.opcode == jump_or_pop: + inst = self.follow_jump(inst.argval) + + if inst.opcode == pop_and_jump: + pop = True + inst = self.follow_jump(inst.argval) + + if inst.opcode in pop_and_no_jump: + pop = True + inst = self.follow_jump(inst.offset + 2) + + return (pop_and_jump if pop else jump_or_pop, inst.offset) + + def optimize_tuple(self, inst: dis.Instruction) -> Optional[Tuple[int, Any, int]]: + """ + If tuples are loaded with LOAD_CONST or BUILD_TUPLE depends on linenumbers. + This function performes this optimization independent of the line numbers. + """ + if inst.opcode == opcodes.LOAD_CONST and not sys.version_info >= (3, 10): + values = [] + while inst.opcode in (opcodes.LOAD_CONST, opcodes.EXTENDED_ARG): + if inst.opcode == opcodes.LOAD_CONST: + values.append(inst.argval) + + if inst.offset // 2 == len(self.instructions): + break + inst = self.instructions[inst.offset // 2 + 1] + + # BUILD_LIST is not 100% correct here but this saves us + # from performance problems on 3.8 in test_extended_arg() + # [1,2,3,4,5] would trigger the optimization for every LOAD_CONST, + # which would lead to a quadratic complexity + if inst.opcode in ( + opcodes.BUILD_TUPLE, + opcodes.BUILD_LIST, + ) and inst.argval == len(values): + return (opcodes.LOAD_CONST, tuple(values), inst.offset + 2) + + return None + + +def merge_code_fingerprint( + code_a: CodeFingerprint, code_b: CodeFingerprint +) -> Iterator[Tuple[InstFingerprint, InstFingerprint]]: + done = set() + if code_a.start is None or code_b.start is None: + return + + todo: List[Tuple[int, int]] = [(code_a.start, code_b.start)] + + while todo: + a, b = todo.pop() + inst_a = code_a.graph[a] + inst_b = code_b.graph[b] + yield (inst_a, inst_b) + done.add((a, b)) + + if TESTING and inst_a.opcode != inst_b.opcode: # pragma: no cover + print("code:") + for ia, ib in zip(code_a.instructions, code_b.instructions): + print( + ia.offset, + ia.opname.ljust(20), + "==" if ia.opname == ib.opname else "!=", + ib.opname, + ) + + assert inst_a.opcode == inst_b.opcode, (inst_a, inst_b) + + todo += [key for key in zip_all(inst_a.next, inst_b.next) if key not in done] + + +T1 = TypeVar("T1") +T2 = TypeVar("T2") + + +def zip_all(a: Sequence[T1], b: Sequence[T2]) -> Iterable[Tuple[T1, T2]]: + assert len(a) == len(b), "length mismatch" + + return zip(a, b) + + +class CodeMap: + def __init__( + self, original_tree: EnhancedAST, filename: str, *, rewrite: Any = None + ): + sys.setrecursionlimit(5000) + + if rewrite is not None: + original_tree = copy.deepcopy(original_tree) + + index_tree = copy.deepcopy(original_tree) + + self.lineno_map: Dict[int, Optional[int]] = {} + self.node_map: Dict[int, ast.AST] = {} + + self.instructions: Dict[CodeType, List[dis.Instruction]] = {} + + # add index to every node + for index, (index_node, original_node) in enumerate( + zip(ast.walk(index_tree), ast.walk(original_tree)) + ): + assert type(index_node) == type(original_node) + + start_index = index * 2 + end_index = start_index + 1 + + if hasattr(index_node, "lineno"): + assert index_node.lineno == original_node.lineno + + self.lineno_map[start_index] = index_node.lineno + self.lineno_map[end_index] = index_node.end_lineno + + self.node_map[start_index] = original_node + self.node_map[end_index] = original_node + + index_node.lineno = start_index + index_node.end_lineno = end_index + index_node.col_offset = 0 + index_node.end_col_offset = 0 + + # rewrite the ast in the same way some other tools do (pytest for example) + if rewrite is not None: + rewrite(index_tree) + rewrite(original_tree) + + # compile the code + # the inst.starts_line contains now the index + original_bc = compile( + cast(ast.Module, original_tree), filename, "exec", dont_inherit=True + ) + index_bc = compile( + cast(ast.Module, index_tree), filename, "exec", dont_inherit=True + ) + + # create a code map where every code-block can be found + # key is not unique but a good heuristic to speed up the search + self.code_map: Dict[CodeType, List[Match]] = defaultdict(list) + + self.code_key_cache: Dict[CodeType, CodeFingerprint] = {} + + self.todo: Dict[int, List[Tuple[CodeType, CodeType]]] = defaultdict(list) + self.todo[original_bc.co_firstlineno] = [(original_bc, index_bc)] + + self.code_lines: Set[int] = set() + + def handle(self, line: int) -> None: + while line in self.todo or line not in self.code_lines: + parent_line = max(l for l in self.todo if l <= line) + + for original_code, index_code in self.todo.pop(parent_line): + original_key = CodeFingerprint(original_code) + index_key = CodeFingerprint(index_code, lineno_map=self.lineno_map.get) + + offset_to_index = {} + + for original_instr, index_instr in merge_code_fingerprint( + original_key, index_key + ): + if index_instr.index is not None: + # TODO: why None + offset_to_index[original_instr.offset] = index_instr.index + + if isinstance(original_instr.value, CodeType): + assert isinstance(index_instr.value, CodeType) + self.todo[original_instr.value.co_firstlineno].append( + (original_instr.value, index_instr.value) + ) + + original_instructions = original_key.instructions + + nodes = [ + None + if inst.offset not in offset_to_index + else self.node_map[offset_to_index[inst.offset]] + for inst in original_instructions + ] + + match = Match(nodes, original_code, index_code) + + self.code_map[original_code].append(match) + self.instructions[original_code] = original_instructions + + self.code_lines.add(original_code.co_firstlineno) + + def __getitem__(self, code: CodeType) -> Tuple[List[Match], List[dis.Instruction]]: + self.handle(code.co_firstlineno) + + if code in self.code_map: + return self.code_map[code], self.instructions[code] + else: + # this code tries to find code which contains nan (not a number) in the co_consts + # of the code object + # nan or something which contains nan (like code objects) can not be used + # as key for a dict lookup, because `nan != nan` + + # performance is not so important here because this case is really unlikely + code_key = normalize(code) + for key, value in self.code_map.items(): + if normalize(key) == code_key: + return (value, self.instructions[key]) + + return ([], []) + + +def is_module_rewritten_by_pytest(frame: FrameType) -> bool: + global_vars = frame.f_globals.keys() + return "@py_builtins" in global_vars or "@pytest_ar" in global_vars + + +class IndexNodeFinder(BaseNodeFinder): + """ + Mapping bytecode to ast-node based on the source positions, which where introduced in pyhon 3.11. + In general every ast-node can be exactly referenced by its begin/end line/col_offset, which is stored in the bytecode. + There are only some exceptions for methods and attributes. + """ + + @staticmethod + @lru_cache(100) + def get_code_map(tree: EnhancedAST, filename: str) -> CodeMap: + return CodeMap(tree, filename) + + @staticmethod + @lru_cache(100) + def get_code_map_pytest(tree: EnhancedAST, filename: str, text: str) -> CodeMap: + return CodeMap( + tree, filename, rewrite=lambda tree: pytest_rewrite_assert(tree, text) + ) + + def __init__( + self, + frame: FrameType, + stmts: Set[EnhancedAST], + tree: ast.Module, + lasti: int, + source: Source, + ): + # maybe pytest has rewritten the assertions + if is_module_rewritten_by_pytest(frame): + code_map = self.get_code_map_pytest(tree, source.text, source.filename) + else: + code_map = self.get_code_map(tree, source.filename) + + # lookup the current frame in the code_map + matches, instructions = code_map[frame.f_code] + + if not matches: # pragma: no cover + raise NotOneValueFound("no match found") + + while instructions[lasti // 2].opcode == opcodes.EXTENDED_ARG: + lasti += 2 + instruction = instructions[lasti // 2] + + if len(matches) == 1: + match = matches[0] + else: + # in code for this generator expression ends up twice in the bytecode + # might be a bug + # while (t for t in s): + # pass + + if all_equal(m.index_code for m in matches): + match = matches[0] + else: + nodes = [ + self.get_node(match, instructions, lasti, tree)[0] + for match in matches + ] + raise MultipleMatches(nodes) + + if instructions[lasti // 2].opcode == opcodes.NOP: + raise KnownIssue("can not map NOP") + + self.result, self.decorator = self.get_node(match, instructions, lasti, tree) + + self.known_issues(self.result, instruction, instructions, frame.f_code.co_name) + + if self.decorator is None: + self.verify(self.result, instruction) + else: + assert_(self.decorator in cast(ast.FunctionDef, self.result).decorator_list) + + def known_issues( + self, + node: EnhancedAST, + instruction: dis.Instruction, + instructions: List[dis.Instruction], + co_name: str, + ) -> None: + inst_index = instruction.offset // 2 + + # known issues + if self.is_except_cleanup(instruction, node): + raise KnownIssue( + "exeption cleanup does not belong to the last node in a except block" + ) + + if sys.version_info >= (3, 10) and isinstance(self.result, ast.pattern): + raise KnownIssue("there are some wired bugs in pattern locations") + + if isinstance(self.result, ast.Assert): + raise KnownIssue("assert matched") + + compare_opcodes = ( + (opcodes.COMPARE_OP, opcodes.CONTAINS_OP, opcodes.IS_OP) + if sys.version_info >= (3, 9) + else (opcodes.COMPARE_OP,) + ) + + if instruction.opcode in compare_opcodes and not isinstance(node, ast.Compare): + raise KnownIssue("COMPARE_OP seems broken") + + if ( + sys.version_info >= (3, 8) + and not sys.version_info >= (3, 9) + and instruction.opcode in (opcodes.CALL_FINALLY,) + ): + raise KnownIssue("CALL_FINALLY seems broken") + + if ( + sys.version_info >= (3, 8) + and not sys.version_info >= (3, 9) + and instruction.opcode in (opcodes.WITH_CLEANUP_START,) + ): + raise KnownIssue("call to __exit__ can not be mapped") + + if ( + sys.version_info >= (3, 9) + and not sys.version_info >= (3, 10) + and instruction.opcode == opcodes.CALL_FUNCTION + and not isinstance(node, ast.Call) + and not isinstance(node, (ast.With, ast.AsyncWith)) + and has_parent(node, (ast.With, ast.AsyncWith)) + ): + # __exit__ can not be mapped + + patterns = [ + [ + opcodes.POP_BLOCK, + opcodes.LOAD_CONST, + opcodes.DUP_TOP, + opcodes.DUP_TOP, + opcodes.CALL_FUNCTION, + ], + [ + opcodes.POP_BLOCK, + opcodes.ROT_TWO, + opcodes.LOAD_CONST, + opcodes.DUP_TOP, + opcodes.DUP_TOP, + opcodes.CALL_FUNCTION, + ], + ] + for pattern in patterns: + if inst_index >= len(pattern) - 1: + exit_instructions = [ + i.opcode + for i in instructions[: inst_index + 1] + if i.opcode != opcodes.EXTENDED_ARG + ][-len(pattern) :] + if exit_instructions == pattern: + raise KnownIssue("call to __exit__ can not be mapped") + + if ( + # todo find correct version + sys.version_info[:2] == (3, 10) + and isinstance(node, ast.Compare) + and instruction.opcode == opcodes.CALL_FUNCTION + and any(isinstance(n, ast.Assert) for n in node_and_parents(node)) + ): + raise KnownIssue( + "known bug in 3.11.1 https://github.com/python/cpython/issues/95921" + ) + + if self.in_class_prelog( + instructions, co_name, instruction.offset + ) and not isinstance(node, ast.ClassDef): + raise KnownIssue( + "header of class with decorators has wrong source positions" + ) + + if ( + instruction.opcode == opcodes.STORE_NAME + and instruction.argval == "__classcell__" + ): + raise KnownIssue("store __classcell__") + + def get_node( + self, + match: Match, + instructions: List[dis.Instruction], + lasti: int, + tree: ast.Module, + ) -> Tuple[EnhancedAST, Union[ast.AST, None]]: + index = lasti // 2 + + result = match.nodes[index] + decorator = None + + instruction = instructions[index] + + if result is None: + if instruction.opcode in (opcodes.NOP, opcodes.JUMP_ABSOLUTE): + raise KnownIssue("%s can not be mapped" % instruction.opname) + else: + raise NotOneValueFound("could not match %s" % instruction.opname) + + # workaround for method calls + # thing.method() + # ^ end_lineno + # end_lineno of the attribute is used by the python compiler + # for the linenumber of the CALL and CALL_METHOD instruction + # !!! the same end_lineno is used for starts_line of `thing` + if sys.version_info >= (3, 10): + if instructions[lasti // 2].opcode == opcodes.CALL_METHOD: + if not isinstance(result, ast.JoinedStr): + assert isinstance(result.parent, ast.Call), (result, result.parent) + result = result.parent + + elif ( + sys.version_info >= (3, 8) + and instructions[lasti // 2].opcode == opcodes.LOAD_METHOD + and isinstance(result, ast.Call) + ): + result = result.func + + # detecting decorators + index = lasti // 2 + if ( + isinstance(result, (ast.ClassDef, function_node_types)) + and instructions[index].opcode == opcodes.CALL_FUNCTION + ): + decorator_index = 0 + while instructions[index].opcode == opcodes.CALL_FUNCTION: + index += 1 + while instructions[index].opcode == opcodes.EXTENDED_ARG: + index += 1 + + decorator_index += 1 + decorator_index -= 1 + if 0 <= decorator_index < len(result.decorator_list) and instructions[ + index + ].opname.startswith("STORE_"): + decorator = result.decorator_list[decorator_index] + + return result, decorator + + def in_class_prelog( + self, instructions: List[dis.Instruction], code_name: str, offset: int + ) -> bool: + prelog = [ + (0, opcodes.LOAD_NAME, "__name__"), + (2, opcodes.STORE_NAME, "__module__"), + (4, opcodes.LOAD_CONST, code_name), + (6, opcodes.STORE_NAME, "__qualname__"), + ] + code_start = [ + ( + instruction.offset, + instruction.opcode, + isinstance(instruction.argval, str) + and instruction.argval.split(".")[-1], + ) + for instruction in instructions[: len(prelog)] + ] + + return prelog == code_start and offset // 2 < len(prelog) diff --git a/executing/_position_node_finder.py b/executing/_position_node_finder.py index e5cddc3..1161698 100644 --- a/executing/_position_node_finder.py +++ b/executing/_position_node_finder.py @@ -5,68 +5,12 @@ from typing import Any, Callable, Iterator, Optional, Sequence, Set, Tuple, Type, Union, cast from .executing import EnhancedAST, NotOneValueFound, Source, only, function_node_types, assert_ from ._exceptions import KnownIssue, VerifierFailure +from ._base_node_finder import BaseNodeFinder, node_and_parents, parents from functools import lru_cache -# the code in this module can use all python>=3.11 features - - -def parents(node: EnhancedAST) -> Iterator[EnhancedAST]: - while True: - if hasattr(node, "parent"): - node = node.parent - yield node - else: - break # pragma: no mutate - - -def node_and_parents(node: EnhancedAST) -> Iterator[EnhancedAST]: - yield node - yield from parents(node) - - -def mangled_name(node: EnhancedAST) -> str: - """ - - Parameters: - node: the node which should be mangled - name: the name of the node - - Returns: - The mangled name of `node` - """ - if isinstance(node, ast.Attribute): - name = node.attr - elif isinstance(node, ast.Name): - name = node.id - elif isinstance(node, (ast.alias)): - name = node.asname or node.name.split(".")[0] - elif isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): - name = node.name - elif isinstance(node, ast.ExceptHandler): - assert node.name - name = node.name - else: - raise TypeError("no node to mangle") - - if name.startswith("__") and not name.endswith("__"): - - parent,child=node.parent,node - - while not (isinstance(parent,ast.ClassDef) and child not in parent.bases): - if not hasattr(parent,"parent"): - break # pragma: no mutate - - parent,child=parent.parent,parent - else: - class_name=parent.name.lstrip("_") - if class_name!="": - return "_" + class_name + name - - - - return name +assert sys.version_info >= (3,11), "the code in this module supports only python>=3.11" @lru_cache(128) # pragma: no mutate def get_instructions(code: CodeType) -> list[dis.Instruction]: @@ -87,24 +31,9 @@ def get_instructions(code: CodeType) -> list[dis.Instruction]: ast.GeneratorExp, ) -op_type_map = { - "**": ast.Pow, - "*": ast.Mult, - "@": ast.MatMult, - "//": ast.FloorDiv, - "/": ast.Div, - "%": ast.Mod, - "+": ast.Add, - "-": ast.Sub, - "<<": ast.LShift, - ">>": ast.RShift, - "&": ast.BitAnd, - "^": ast.BitXor, - "|": ast.BitOr, -} - - -class PositionNodeFinder(object): + + +class PositionNodeFinder(BaseNodeFinder): """ Mapping bytecode to ast-node based on the source positions, which where introduced in pyhon 3.11. In general every ast-node can be exactly referenced by its begin/end line/col_offset, which is stored in the bytecode. @@ -279,313 +208,13 @@ def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None: raise KnownIssue("store __classcell__") - @staticmethod - def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool: - if inst.opname not in ( - "STORE_NAME", - "STORE_FAST", - "STORE_DEREF", - "STORE_GLOBAL", - "DELETE_NAME", - "DELETE_FAST", - "DELETE_DEREF", - "DELETE_GLOBAL", - ): - return False - - # This bytecode does something exception cleanup related. - # The position of the instruciton seems to be something in the last ast-node of the ExceptHandler - # this could be a bug, but it might not be observable in normal python code. - - # example: - # except Exception as exc: - # enum_member._value_ = value - - # other example: - # STORE_FAST of e was mapped to Constant(value=False) - # except OSError as e: - # if not _ignore_error(e): - # raise - # return False - - # STORE_FAST of msg was mapped to print(...) - # except TypeError as msg: - # print("Sorry:", msg, file=file) - - if ( - isinstance(node, ast.Name) - and isinstance(node.ctx,ast.Store) - and inst.opname.startswith("STORE_") - and mangled_name(node) == inst.argval - ): - # Storing the variable is valid and no exception cleanup, if the name is correct - return False - - if ( - isinstance(node, ast.Name) - and isinstance(node.ctx,ast.Del) - and inst.opname.startswith("DELETE_") - and mangled_name(node) == inst.argval - ): - # Deleting the variable is valid and no exception cleanup, if the name is correct - return False - - return any( - isinstance(n, ast.ExceptHandler) and n.name and mangled_name(n) == inst.argval - for n in parents(node) - ) - - def verify(self, node: EnhancedAST, instruction: dis.Instruction) -> None: - """ - checks if this node could gererate this instruction - """ - - op_name = instruction.opname - extra_filter: Callable[[EnhancedAST], bool] = lambda e: True - ctx: Type = type(None) - - def inst_match(opnames: Union[str, Sequence[str]], **kwargs: Any) -> bool: - """ - match instruction - - Parameters: - opnames: (str|Seq[str]): inst.opname has to be equal to or in `opname` - **kwargs: every arg has to match inst.arg - - Returns: - True if all conditions match the instruction - - """ - - if isinstance(opnames, str): - opnames = [opnames] - return instruction.opname in opnames and kwargs == { - k: getattr(instruction, k) for k in kwargs - } - - def node_match(node_type: Union[Type, Tuple[Type, ...]], **kwargs: Any) -> bool: - """ - match the ast-node - - Parameters: - node_type: type of the node - **kwargs: every `arg` has to be equal `node.arg` - or `node.arg` has to be an instance of `arg` if it is a type. - """ - return isinstance(node, node_type) and all( - isinstance(getattr(node, k), v) - if isinstance(v, type) - else getattr(node, k) == v - for k, v in kwargs.items() - ) - - if op_name == "CACHE": - return - - if inst_match("CALL") and node_match((ast.With, ast.AsyncWith)): - # call to context.__exit__ - return - - if inst_match(("CALL", "LOAD_FAST")) and node_match( - (ast.ListComp, ast.GeneratorExp, ast.SetComp, ast.DictComp) - ): - # call to the generator function - return - - if inst_match(("CALL", "CALL_FUNCTION_EX")) and node_match( - (ast.ClassDef, ast.Call) - ): - return - - if inst_match(("COMPARE_OP", "IS_OP", "CONTAINS_OP")) and node_match( - ast.Compare - ): - return - - if inst_match("LOAD_NAME", argval="__annotations__") and node_match( - ast.AnnAssign - ): - return - if ( - ( - inst_match("LOAD_METHOD", argval="join") - or inst_match(("CALL", "BUILD_STRING")) - ) - and node_match(ast.BinOp, left=ast.Constant, op=ast.Mod) - and isinstance(cast(ast.Constant, cast(ast.BinOp, node).left).value, str) - ): - # "..."%(...) uses "".join - return - - if inst_match("STORE_SUBSCR") and node_match(ast.AnnAssign): - # data: int - return - - - if inst_match(("DELETE_NAME", "DELETE_FAST")) and node_match( - ast.Name, id=instruction.argval, ctx=ast.Del - ): - return - - if inst_match("BUILD_STRING") and ( - node_match(ast.JoinedStr) or node_match(ast.BinOp, op=ast.Mod) - ): - return - - if inst_match(("BEFORE_WITH","WITH_EXCEPT_START")) and node_match(ast.With): - return - - if inst_match(("STORE_NAME", "STORE_GLOBAL"), argval="__doc__") and node_match( - ast.Constant - ): - # store docstrings - return - - if ( - inst_match(("STORE_NAME", "STORE_FAST", "STORE_GLOBAL", "STORE_DEREF")) - and node_match(ast.ExceptHandler) - and instruction.argval == mangled_name(node) - ): - # store exception in variable - return - - if ( - inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL")) - and node_match((ast.Import, ast.ImportFrom)) - and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names) - ): - # store imported module in variable - return - - if ( - inst_match(("STORE_FAST", "STORE_DEREF", "STORE_NAME", "STORE_GLOBAL")) - and ( - node_match((ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)) - or node_match( - ast.Name, - ctx=ast.Store, - ) - ) - and instruction.argval == mangled_name(node) - ): - return - - if False: - # TODO: match expressions are not supported for now - if inst_match(("STORE_FAST", "STORE_NAME")) and node_match( - ast.MatchAs, name=instruction.argval - ): - return - - if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchSequence): - return - - if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchValue): - return - - if inst_match("BINARY_OP") and node_match( - ast.AugAssign, op=op_type_map[instruction.argrepr.removesuffix("=")] - ): - # a+=5 - return - - if node_match(ast.Attribute, ctx=ast.Del) and inst_match( - "DELETE_ATTR", argval=mangled_name(node) - ): - return - - if inst_match(("JUMP_IF_TRUE_OR_POP", "JUMP_IF_FALSE_OR_POP")) and node_match( - ast.BoolOp - ): - # and/or short circuit - return - - if inst_match("DELETE_SUBSCR") and node_match(ast.Subscript, ctx=ast.Del): - return - - if ( - node_match(ast.Name, ctx=ast.Load) - or ( - node_match(ast.Name, ctx=ast.Store) - and isinstance(node.parent, ast.AugAssign) - ) - ) and inst_match( - ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL", "LOAD_DEREF"), argval=mangled_name(node) - ): - return - - if node_match(ast.Name, ctx=ast.Del) and inst_match( - ("DELETE_NAME", "DELETE_GLOBAL", "DELETE_DEREF"), argval=mangled_name(node) - ): - return - - # old verifier - - typ: Type = type(None) - op_type: Type = type(None) - - if op_name.startswith(("BINARY_SUBSCR", "SLICE+")): - typ = ast.Subscript - ctx = ast.Load - elif op_name.startswith("BINARY_"): - typ = ast.BinOp - op_type = op_type_map[instruction.argrepr] - extra_filter = lambda e: isinstance(cast(ast.BinOp, e).op, op_type) - elif op_name.startswith("UNARY_"): - typ = ast.UnaryOp - op_type = dict( - UNARY_POSITIVE=ast.UAdd, - UNARY_NEGATIVE=ast.USub, - UNARY_NOT=ast.Not, - UNARY_INVERT=ast.Invert, - )[op_name] - extra_filter = lambda e: isinstance(cast(ast.UnaryOp, e).op, op_type) - elif op_name in ("LOAD_ATTR", "LOAD_METHOD", "LOOKUP_METHOD"): - typ = ast.Attribute - ctx = ast.Load - extra_filter = lambda e: mangled_name(e) == instruction.argval - elif op_name in ( - "LOAD_NAME", - "LOAD_GLOBAL", - "LOAD_FAST", - "LOAD_DEREF", - "LOAD_CLASSDEREF", + instruction.opname == "CALL" + and not isinstance(node, ast.Call) + and any(isinstance(p, ast.Assert) for p in parents(node)) + and sys.version_info >= (3, 11, 2) ): - typ = ast.Name - ctx = ast.Load - extra_filter = lambda e: cast(ast.Name, e).id == instruction.argval - elif op_name in ("COMPARE_OP", "IS_OP", "CONTAINS_OP"): - typ = ast.Compare - extra_filter = lambda e: len(cast(ast.Compare, e).ops) == 1 - elif op_name.startswith(("STORE_SLICE", "STORE_SUBSCR")): - ctx = ast.Store - typ = ast.Subscript - elif op_name.startswith("STORE_ATTR"): - ctx = ast.Store - typ = ast.Attribute - extra_filter = lambda e: mangled_name(e) == instruction.argval - - node_ctx = getattr(node, "ctx", None) - - ctx_match = ( - ctx is not type(None) - or not hasattr(node, "ctx") - or isinstance(node_ctx, ctx) - ) - - # check for old verifier - if isinstance(node, typ) and ctx_match and extra_filter(node): - return - - # generate error - - title = "ast.%s is not created from %s" % ( - type(node).__name__, - instruction.opname, - ) - - raise VerifierFailure(title, node, instruction) + raise KnownIssue("exception generation maps to condition") def instruction(self, index: int) -> dis.Instruction: return self.bc_list[index // 2] diff --git a/executing/executing.py b/executing/executing.py index c28091d..5f53a0e 100644 --- a/executing/executing.py +++ b/executing/executing.py @@ -47,6 +47,8 @@ if sys.version_info[0] == 3: function_node_types += (ast.AsyncFunctionDef,) +use_new_algo=sys.version_info>=(3,8) + if sys.version_info[0] == 3: # noinspection PyUnresolvedReferences @@ -375,7 +377,8 @@ def executing(cls, frame_or_tb): assert stmts is not None if node: new_stmts = {statement_containing_node(node)} - assert_(new_stmts <= stmts) + #assert_(new_stmts <= stmts) + # todo what does <= mean hear stmts = new_stmts executing_cache[key] = args = source, node, stmts, decorator @@ -808,15 +811,6 @@ def matching_nodes(self, exprs): finally: setter(expr) - if sys.version_info >= (3, 10): - try: - handle_jumps(instructions, original_instructions) - except Exception: - # Give other candidates a chance - if TESTING or expr_index < len(exprs) - 1: - continue - raise - indices = [ i for i, instruction in enumerate(instructions) @@ -935,188 +929,6 @@ def get_actual_current_instruction(self, lasti): -def non_sentinel_instructions(instructions, start): - # type: (List[EnhancedInstruction], int) -> Iterator[Tuple[int, EnhancedInstruction]] - """ - Yields (index, instruction) pairs excluding the basic - instructions introduced by the sentinel transformation - """ - skip_power = False - for i, inst in islice(enumerate(instructions), start, None): - if inst.argval == sentinel: - assert_(inst.opname == "LOAD_CONST") - skip_power = True - continue - elif skip_power: - assert_(inst.opname == "BINARY_POWER") - skip_power = False - continue - yield i, inst - - -def walk_both_instructions(original_instructions, original_start, instructions, start): - # type: (List[EnhancedInstruction], int, List[EnhancedInstruction], int) -> Iterator[Tuple[int, EnhancedInstruction, int, EnhancedInstruction]] - """ - Yields matching indices and instructions from the new and original instructions, - leaving out changes made by the sentinel transformation. - """ - original_iter = islice(enumerate(original_instructions), original_start, None) - new_iter = non_sentinel_instructions(instructions, start) - inverted_comparison = False - while True: - try: - original_i, original_inst = next(original_iter) - new_i, new_inst = next(new_iter) - except StopIteration: - return - if ( - inverted_comparison - and original_inst.opname != new_inst.opname == "UNARY_NOT" - ): - new_i, new_inst = next(new_iter) - inverted_comparison = ( - original_inst.opname == new_inst.opname in ("CONTAINS_OP", "IS_OP") - and original_inst.arg != new_inst.arg # type: ignore[attr-defined] - ) - yield original_i, original_inst, new_i, new_inst - - -def handle_jumps(instructions, original_instructions): - # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> None - """ - Transforms instructions in place until it looks more like original_instructions. - This is only needed in 3.10+ where optimisations lead to more drastic changes - after the sentinel transformation. - Replaces JUMP instructions that aren't also present in original_instructions - with the sections that they jump to until a raise or return. - In some other cases duplication found in `original_instructions` - is replicated in `instructions`. - """ - while True: - for original_i, original_inst, new_i, new_inst in walk_both_instructions( - original_instructions, 0, instructions, 0 - ): - if opnames_match(original_inst, new_inst): - continue - - if "JUMP" in new_inst.opname and "JUMP" not in original_inst.opname: - # Find where the new instruction is jumping to, ignoring - # instructions which have been copied in previous iterations - start = only( - i - for i, inst in enumerate(instructions) - if inst.offset == new_inst.argval - and not getattr(inst, "_copied", False) - ) - # Replace the jump instruction with the jumped to section of instructions - # That section may also be deleted if it's not similarly duplicated - # in original_instructions - new_instructions = handle_jump( - original_instructions, original_i, instructions, start - ) - assert new_instructions is not None - instructions[new_i : new_i + 1] = new_instructions - else: - # Extract a section of original_instructions from original_i to return/raise - orig_section = [] - for section_inst in original_instructions[original_i:]: - orig_section.append(section_inst) - if section_inst.opname in ("RETURN_VALUE", "RAISE_VARARGS"): - break - else: - # No return/raise - this is just a mismatch we can't handle - raise AssertionError - - instructions[new_i:new_i] = only(find_new_matching(orig_section, instructions)) - - # instructions has been modified, the for loop can't sensibly continue - # Restart it from the beginning, checking for other issues - break - - else: # No mismatched jumps found, we're done - return - - -def find_new_matching(orig_section, instructions): - # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> Iterator[List[EnhancedInstruction]] - """ - Yields sections of `instructions` which match `orig_section`. - The yielded sections include sentinel instructions, but these - are ignored when checking for matches. - """ - for start in range(len(instructions) - len(orig_section)): - indices, dup_section = zip( - *islice( - non_sentinel_instructions(instructions, start), - len(orig_section), - ) - ) - if len(dup_section) < len(orig_section): - return - if sections_match(orig_section, dup_section): - yield instructions[start:indices[-1] + 1] - - -def handle_jump(original_instructions, original_start, instructions, start): - # type: (List[EnhancedInstruction], int, List[EnhancedInstruction], int) -> Optional[List[EnhancedInstruction]] - """ - Returns the section of instructions starting at `start` and ending - with a RETURN_VALUE or RAISE_VARARGS instruction. - There should be a matching section in original_instructions starting at original_start. - If that section doesn't appear elsewhere in original_instructions, - then also delete the returned section of instructions. - """ - for original_j, original_inst, new_j, new_inst in walk_both_instructions( - original_instructions, original_start, instructions, start - ): - assert_(opnames_match(original_inst, new_inst)) - if original_inst.opname in ("RETURN_VALUE", "RAISE_VARARGS"): - inlined = deepcopy(instructions[start : new_j + 1]) - for inl in inlined: - inl._copied = True - orig_section = original_instructions[original_start : original_j + 1] - if not check_duplicates( - original_start, orig_section, original_instructions - ): - instructions[start : new_j + 1] = [] - return inlined - - return None - - -def check_duplicates(original_i, orig_section, original_instructions): - # type: (int, List[EnhancedInstruction], List[EnhancedInstruction]) -> bool - """ - Returns True if a section of original_instructions starting somewhere other - than original_i and matching orig_section is found, i.e. orig_section is duplicated. - """ - for dup_start in range(len(original_instructions)): - if dup_start == original_i: - continue - dup_section = original_instructions[dup_start : dup_start + len(orig_section)] - if len(dup_section) < len(orig_section): - return False - if sections_match(orig_section, dup_section): - return True - - return False - -def sections_match(orig_section, dup_section): - # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> bool - """ - Returns True if the given lists of instructions have matching linenos and opnames. - """ - return all( - ( - orig_inst.lineno == dup_inst.lineno - # POP_BLOCKs have been found to have differing linenos in innocent cases - or "POP_BLOCK" == orig_inst.opname == dup_inst.opname - ) - and opnames_match(orig_inst, dup_inst) - for orig_inst, dup_inst in zip(orig_section, dup_section) - ) - - def opnames_match(inst1, inst2): # type: (Instruction, Instruction) -> bool return ( @@ -1252,9 +1064,10 @@ def node_linenos(node): for lineno in linenos: yield lineno - if sys.version_info >= (3, 11): from ._position_node_finder import PositionNodeFinder as NodeFinder +elif sys.version_info >= (3, 8): + from ._index_node_finder import IndexNodeFinder as NodeFinder else: - NodeFinder = SentinelNodeFinder + NodeFinder = SentinelNodeFinder # type: ignore[misc,assignment] diff --git a/setup.cfg b/setup.cfg index d22b4e2..7e5ab1e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,10 @@ install_requires = [options.extras_require] tests= asttokens>=2.1.0 + ipython pytest + coverage + coverage-enable-subprocess littleutils rich; python_version >='3.11' @@ -41,6 +44,17 @@ executing = py.typed [coverage:run] relative_files = True +input = executing/executing.py +parallel = true +branch = true + +[coverage:report] +exclude_lines= + pragma: no cover + assert False [bdist_wheel] universal=1 + +[tool:pytest] +xfail_strict=true diff --git a/tests/analyse.py b/tests/analyse.py index 15b8da4..59f6e21 100644 --- a/tests/analyse.py +++ b/tests/analyse.py @@ -2,9 +2,10 @@ import pathlib import dis import types -import inspect from executing import Source +from executing._exceptions import KnownIssue,VerifierFailure,NoMatch +from executing import NotOneValueFound import executing executing.executing.TESTING = 1 @@ -12,130 +13,245 @@ from rich import print as rprint import rich - -class Frame: - pass - - -if len(sys.argv) <= 1 or sys.argv[1] in ("--help", "-h"): - print( - """ -analyse.py [line | first_line:last_line] - -Analyses a range in the given source in the specified range -and maps every bytecode to the node found by executing. -""" - ) - sys.exit(0) - -filename = pathlib.Path(sys.argv[1]) - -if ":" in sys.argv[2]: - start, end = sys.argv[2].split(":") - start = int(start) - end = int(end) -else: - start = end = int(sys.argv[2]) - - -code = filename.read_text() - - -def inspect_opcode(bytecode, index, lineno): - frame = Frame() - frame.f_lasti = index - frame.f_code = bytecode - frame.f_globals = globals() - frame.f_lineno = lineno - source = Source.for_frame(frame) - - try: - ex = Source.executing(frame) - result = "[green]" + type(ex.node).__name__ - if hasattr(ex.node, "name"): - result += "(" + ex.node.name + ")" - elif hasattr(ex.node, "id"): - result += "(" + ex.node.id + ")" - elif hasattr(ex.node, "attr"): - result += "(." + ex.node.attr + ")" - elif hasattr(ex.node, "value"): - result += f"({ex.node.value})" - - if ex.decorator: - result += " @%s" % ex.decorator - return result - except RuntimeError: - raise - except Exception as e: - return "[red]" + type(e).__name__ + ": " + str(e).split("\n")[0] - - +import traceback import rich.syntax from rich.console import Console from rich.table import Table, Column from rich.highlighter import ReprHighlighter -console = Console() +import ast +from deadcode import is_deadcode -console.print( - rich.syntax.Syntax(code, "python", line_numbers=True, line_range=(start, end)) -) +from cProfile import Profile -print("all bytecodes in this range:") +class Frame: + pass -bc = compile(code, filename, "exec") +class App: + def inspect_opcode(self, bytecode, index, lineno): + frame = Frame() + frame.f_lasti = index + frame.f_code = bytecode + frame.f_globals = globals() + frame.f_lineno = lineno + + self.profile.enable() + source = Source.for_frame(frame) + + try: + ex = Source.executing(frame) + result = "[green]" + type(ex.node).__name__ + if hasattr(ex.node, "name"): + result += "(" + str(ex.node.name) + ")" + elif hasattr(ex.node, "id"): + result += "(" + ex.node.id + ")" + elif hasattr(ex.node, "attr"): + result += "(." + ex.node.attr + ")" + elif hasattr(ex.node, "value"): + result += f"({ex.node.value})" + + if ex.decorator: + result += " @%s" % ex.decorator + return result + except RuntimeError: + raise + except (KnownIssue,VerifierFailure,NoMatch,NotOneValueFound,AssertionError) as e: + color= "[yellow]" if isinstance(e,KnownIssue) else "[red]" + return color + type(e).__name__ + ": " + str(e).split("\n")[0] + finally: + self.profile.disable() + + def inspect(self, bc): + first = True + table = Table( + title=bc.co_name + ":", + box=None, + title_style="blue", + title_justify="left", + ) + + table.add_column("offset", justify="right") + + if sys.version_info >= (3, 11): + table.add_column("start") + table.add_column("end") + else: + table.add_column("line") + + table.add_column("instruction") + table.add_column("ast-node") + + highlighter = ReprHighlighter() + + starts_line = None + + for i in dis.get_instructions( + bc, **({"show_caches": True} if sys.version_info >= (3, 11) else {}) + ): + if i.starts_line is not None: + starts_line = i.starts_line + + if sys.version_info >= (3, 11): + in_range = ( + i.positions.lineno is not None + and i.positions.lineno <= self.end + and self.start <= i.positions.end_lineno + ) + else: + in_range = ( + starts_line is not None and self.start <= starts_line <= self.end + ) + + if in_range: + if first: + first = False + + ex = self.inspect_opcode(bc, i.offset, starts_line) + + if sys.version_info >= (3, 11): + positions = ( + "%s:%s" % (i.positions.lineno, i.positions.col_offset), + "%s:%s" % (i.positions.end_lineno, i.positions.end_col_offset), + ) + else: + positions = (str(starts_line),) + + table.add_row( + str(i.offset), + *positions, + highlighter("%s(%s)" % (i.opname, i.argval)), + ex, + style="on grey19" if i.opname == "CACHE" else "on grey30" + # **({"style":"on white" } if i.opname=="CACHE" else {}) + ) + + if first == False: + self.console.print() + self.console.print(table) + + for const in bc.co_consts: + if isinstance(const, types.CodeType): + self.inspect(const) + + def dump_deadcode(self, filename): + from rich import print as rprint + from rich.tree import Tree + from rich.syntax import Syntax + + print(filename) + with open(filename) as file: + code = file.read() + tree = ast.parse(code) + + for node in ast.walk(tree): + for child in ast.iter_child_nodes(node): + child.parent = node + + node = tree + + def report(node, tree): + + if isinstance( + node, (ast.expr_context, ast.operator, ast.unaryop, ast.cmpop) + ): + return + + if isinstance(node, (ast.stmt, ast.expr)): + deadcode = is_deadcode(node) + else: + deadcode = None + + if deadcode is None: + deadcode = "[red]" + else: + deadcode = "[red]dead" if deadcode else "[blue]used" + + name = type(node).__name__ + + if isinstance(node, ast.Name): + name += "(%s)" % node.id + + if isinstance(node, ast.Attribute): + name += "(.%s)" % node.attr + + if hasattr(node, "_Deadcode__static_value"): + name += " == %r" % getattr(node, "_Deadcode__static_value") + + t = tree.add("%s %s" % (name, deadcode) + (" %s:%s"%(node.lineno,node.end_lineno) if hasattr(node,"lineno") else "")) + dots = False + + for child in ast.iter_child_nodes(node): + if ( + hasattr(child, "lineno") + and ( child.lineno > self.end + or self.start > child.end_lineno) + ): + if not dots: + tree.add("...") + dots = True + continue + + report(child, t) + dots = False + + tree = Tree("ast") + report(node, tree) + rprint(tree) + + def main(self): + import sys + + if len(sys.argv) <= 1 or sys.argv[1] in ("--help", "-h"): + print( + """ + analyse.py [line | first_line:last_line] + + Analyses a range in the given source in the specified range + and maps every bytecode to the node found by executing. + """ + ) + sys.exit(0) -def inspect(bc): - first = True - table = Table( - title=bc.co_name + ":", - box=None, - title_style="blue", - title_justify="left", - ) - - table.add_column("offset", justify="right") - table.add_column("start") - table.add_column("end") - table.add_column("instruction") - table.add_column("ast-node") + self.profile=Profile() - highlighter=ReprHighlighter() + filename = pathlib.Path(sys.argv[1]) - for i in dis.get_instructions(bc, show_caches=True): + if ":" in sys.argv[2]: + self.start, self.end = sys.argv[2].split(":") + self.start = int(self.start) + self.end = int(self.end) + else: + self.start = self.end = int(sys.argv[2]) - if ( - i.positions.lineno is not None - and i.positions.lineno <= end - and start <= i.positions.end_lineno - ): - if first: - first = False + code = filename.read_text() + self.console = Console() + self.console.print( + rich.syntax.Syntax( + code, "python", line_numbers=True, line_range=(self.start, self.end) + ) + ) - ex = inspect_opcode(bc, i.offset, i.positions.lineno) + print("all bytecodes in this range:") - table.add_row( - str(i.offset), - "%s:%s" % (i.positions.lineno, i.positions.col_offset), - "%s:%s" % (i.positions.end_lineno, i.positions.end_col_offset), - highlighter("%s(%s)" % (i.opname, i.argval)), - ex, - style="on grey19" if i.opname=="CACHE" else "on grey30" - #**({"style":"on white" } if i.opname=="CACHE" else {}) - ) + if 0: + code=ast.parse(code,filename,"exec") + for i,node in enumerate(ast.walk(code)): + node.lineno=i*2 + node.end_lineno=i*2+1 + + bc = compile(code, filename, "exec") + self.inspect(bc) + + self.dump_deadcode(filename) - if first == False: - console.print() - console.print(table) - for const in bc.co_consts: - if isinstance(const, types.CodeType): - inspect(const) + #self.profile.print_stats(sort="cumtime") -inspect(bc) +if __name__ == "__main__": + App().main() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..5e7bb16 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,13 @@ +import pytest + + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_makereport(item, call): + # execute all other hooks to obtain the report object + outcome = yield + rep = outcome.get_result() + + # set a report attribute for each phase of a call, which can + # be "setup", "call", "teardown" + + setattr(item, "rep_" + rep.when, rep) diff --git a/tests/deadcode.py b/tests/deadcode.py index 2dc5b4a..31e76c8 100644 --- a/tests/deadcode.py +++ b/tests/deadcode.py @@ -1,134 +1,430 @@ import ast -import copy -import dis import sys +import operator +py11=sys.version_info>=(3,11) -sentinel_rep = 2 -# generate sentinel at runtime to keep it out of the bytecode -# this allows the algorithm to check also this file -sentinel = "xsglegahghegflgfaih" * sentinel_rep +def contains_break(node_or_list): + "search all child nodes except other loops for a break statement" -def constant(value): - if sys.version_info >= (3, 6): - return ast.Constant(value=value) - elif isinstance(value, int): - return ast.Num(value) - elif isinstance(value, str): - return ast.Str(value) + if isinstance(node_or_list, ast.AST): + childs = ast.iter_child_nodes(node_or_list) + elif isinstance(node_or_list, list): + childs = node_or_list else: - raise TypeError + raise TypeError(node_or_list) + for child in childs: + if isinstance(child, (ast.For, ast.While, ast.AsyncFor)): + if contains_break(child.orelse): + return True + elif isinstance(child, ast.Break): + return True + elif contains_break(child): + return True -def index(value): - if sys.version_info >= (3, 9): - return constant(value) - else: - return ast.Index(constant(value)) - - -class DeadcodeTransformer(ast.NodeTransformer): - def visit(self, node): - constant_type = ( - ast.Constant - if sys.version_info >= (3, 6) - else (ast.Num, ast.Str, ast.Bytes, ast.NameConstant, ast.Ellipsis) - ) - - if getattr(node, "_check_is_deadcode", False): - if isinstance(node, constant_type) and isinstance(node.value, str): - # docstring for example - return constant(sentinel) - - elif isinstance(node, ast.stmt): - return ast.Expr( - value=ast.Call( - func=ast.Name(id="foo", ctx=ast.Load()), - args=[constant(sentinel)], - keywords=[], + return False + + + +class Deadcode: + @staticmethod + def annotate(tree): + deadcode = Deadcode() + + deadcode.annotate_static_values(tree) + deadcode.walk_deadcode(tree, False) + + def __init__(self): + self.future_annotations = False + + operator_map = { + # binary + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.FloorDiv: operator.floordiv, + ast.Mod: operator.mod, + ast.Pow: operator.pow, + ast.LShift: operator.lshift, + ast.RShift: operator.rshift, + ast.BitOr: operator.or_, + ast.BitXor: operator.xor, + ast.BitAnd: operator.and_, + # unary + ast.UAdd: operator.pos, + ast.USub: operator.neg, + ast.Not: operator.not_, + ast.Invert: operator.invert, + } + if hasattr(ast,"MatMult"): + operator_map[ast.MatMult]=operator.matmul + + def annotate_static_values(self, node): + for n in ast.iter_child_nodes(node): + self.annotate_static_values(n) + + try: + if sys.version_info >= (3,6) and isinstance(node, ast.Constant): + node.__static_value = node.value + + if not sys.version_info >= (3,8): + if isinstance(node,ast.Str): + node.__static_value = node.s + if isinstance(node,ast.Bytes): + node.__static_value = node.s + if isinstance(node,ast.Num): + node.__static_value = node.n + if isinstance(node,ast.NameConstant): + node.__static_value = node.value + + + if isinstance(node, ast.Name) and node.id == "__debug__": + node.__static_value = True + + elif isinstance(node, ast.UnaryOp): + try: + node.__static_value = self.operator_map[type(node.op)]( + node.operand.__static_value ) - ) - elif isinstance(node, ast.expr): - if hasattr(node, "ctx") and isinstance(node.ctx, (ast.Store, ast.Del)): - return ast.Subscript( - value=ast.Name(id="foo", ctx=ast.Load()), - slice=index(sentinel), - ctx=node.ctx, + except Exception: + pass + + elif isinstance(node, ast.BinOp): + try: + node.__static_value = self.operator_map[type(node.op)]( + node.left.__static_value, node.right.__static_value ) - else: + if ( + isinstance(node.__static_value, (str, bytes)) + and len(node.__static_value) > 4000 + ): + # do not perform big string operations + # TODO: check if this constraint is correct + del node.__static_value + except Exception: + pass + + elif isinstance(node, ast.Subscript): + try: + node.__static_value = node.value.__static_value[ + node.slice.__static_value + ] + except Exception: + pass + + elif isinstance(node, ast.IfExp): + cnd = self.static_cnd(node.test) + if cnd is True: + node.__static_value = node.body.__static_value + + elif cnd is False: + node.__static_value = node.orelse.__static_value + + elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): + if all(self.static_cnd(n) is True for n in node.values): + node.__static_value = True + + if any(self.static_cnd(n) is False for n in node.values): + node.__static_value = False + + elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): + if all(self.static_cnd(n) is False for n in node.values): + node.__static_value = False + + if any(self.static_cnd(n) is True for n in node.values): + node.__static_value = True + + except AttributeError as e: + if "_Deadcode__static_value" not in str(e): + raise + + def static_cnd(self, node): + try: + return bool(node.__static_value) + except AttributeError: + return None + + def has_static_value(self,node): + try: + node.__static_value + except AttributeError: + return False + return True + + + def static_value(self, node, deadcode): + self.walk_deadcode(node, deadcode) + return self.static_cnd(node) + + def check_stmts(self, stmts, deadcode): + """ + used to check the body: of a function, if, ... + """ + for stmt in stmts: + stmt.deadcode = deadcode + + if self.walk_deadcode(stmt, deadcode): + deadcode = True + return deadcode + + def check_childs(self, childs, deadcode): + """ + used to check childs: function arguments + """ + for child in childs: + self.walk_deadcode(child, deadcode) + + def walk_annotation(self, annotation, deadcode): + if self.future_annotations: + deadcode = True + self.walk_deadcode(annotation, deadcode) + + def walk_deadcode(self, node, deadcode): + "returns True if this statement will never return" + + # this check is not perfect but better than nothing + # it tries to prevent a lot of "node without associated Bytecode" errors + + # They were generated test driven. + # Every case represented here is derived from a error where python performed dead code elimination. + + if node is None: + return + + if isinstance(node, list): + for child in node: + self.walk_deadcode(child, deadcode) + return + + node.deadcode = deadcode or getattr(node, "deadcode", False) + + if isinstance(node, ast.Module): + for stmt in node.body: + if isinstance(stmt, ast.ImportFrom): + if stmt.module == "__future__" and any( + "annotations" == alias.name for alias in stmt.names + ): + self.future_annotations = True + + self.check_stmts(node.body, deadcode) + elif isinstance(node, (ast.With, ast.AsyncWith)): + self.check_childs(node.items, deadcode) + self.check_stmts(node.body, deadcode) + + elif isinstance(node, (ast.Return, ast.Break, ast.Continue, ast.Raise)): + if isinstance(node, ast.Raise): + self.walk_deadcode(node.exc, deadcode) + self.walk_deadcode(node.cause, deadcode) + + if isinstance(node, ast.Return): + self.walk_deadcode(node.value, deadcode) + + deadcode = True + + elif isinstance(node, ast.Assert): + cnd = self.static_value(node.test, deadcode) + + if cnd is False: + node.deadcode = deadcode + self.walk_deadcode(node.msg, deadcode) + deadcode = True + + elif cnd is True: + node.deadcode = deadcode + self.walk_deadcode(node.msg, True) - return ast.Subscript( - value=ast.Tuple( - elts=[node, constant(sentinel)], - ctx=ast.Load(), - ), - slice=index(0), - ctx=ast.Load(), - ) else: - raise TypeError(node) + node.deadcode = deadcode + self.walk_deadcode(node.msg, deadcode) - else: - return super().visit(node) + elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + if sys.version_info >= (3,8): + self.walk_annotation(node.args.posonlyargs, deadcode) + self.walk_annotation(node.args.args, deadcode) + self.walk_annotation(node.args.kwonlyargs, deadcode) -def is_deadcode(node): + self.walk_annotation(node.args.vararg, deadcode) + self.walk_annotation(node.args.kwarg, deadcode) - if isinstance(node, ast.withitem): - node = node.context_expr + self.check_childs(node.args.kw_defaults, deadcode) + self.check_childs(node.args.defaults, deadcode) - if isinstance(node, ast.ExceptHandler): - node = node.body[0] + self.walk_annotation(node.returns, deadcode) + self.check_childs(node.decorator_list, deadcode) - if ( - sys.version_info >= (3, 6) - and isinstance(node.parent, ast.AnnAssign) - and node.parent.target is node - ): - # AnnAssign.target has to be ast.Name - node = node.parent + self.check_stmts(node.body, deadcode) - if hasattr(node, "_is_deadcode"): - return node._is_deadcode + elif isinstance(node, ast.ClassDef): + self.check_childs(node.decorator_list, deadcode) - node._check_is_deadcode = True + self.check_childs(node.bases, deadcode) - module = node - while hasattr(module, "parent"): - module = module.parent + self.check_childs(node.keywords, deadcode) - assert isinstance(module, ast.Module) + self.check_stmts(node.body, deadcode) - # create working copy of the ast - module2 = copy.deepcopy(module) - del node._check_is_deadcode + elif isinstance(node, ast.If): - module2 = ast.fix_missing_locations(DeadcodeTransformer().visit(module2)) + test_value = self.static_value(node.test, deadcode) - try: - code = compile(module2, "", "exec") - except: - print(ast.dump(module2)) - raise + if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) + else_is_dead = self.check_stmts( + node.orelse, deadcode or (test_value is True) + ) - visited = set() + self.walk_deadcode(node.test, deadcode) - def contains_sentinel(code): - if code in visited: - return False + deadcode = if_is_dead and else_is_dead - for inst in dis.get_instructions(code): - arg = inst.argval - if isinstance(arg, type(code)) and contains_sentinel(arg): - return True - if arg == sentinel: - return True + elif sys.version_info >= (3,10) and isinstance(node, ast.Match): + self.walk_deadcode(node.subject, deadcode) + for case_ in node.cases: + case_.deadcode = deadcode + self.walk_deadcode(case_.pattern, deadcode) + self.walk_deadcode(case_.guard, deadcode) + + dead_cases = all( + [self.check_stmts(case_.body, deadcode or self.static_cnd(case_.guard) is False ) for case_ in node.cases] + ) + + if any( + isinstance(case_.pattern, ast.MatchAs) and case_.pattern.pattern is None + for case_ in node.cases + ): + # case _: + deadcode = dead_cases + + elif isinstance(node, (ast.For, ast.AsyncFor)): + self.walk_deadcode(node.target, deadcode) + self.walk_deadcode(node.iter, deadcode) + self.check_stmts(node.body, deadcode) + + else_is_dead = self.check_stmts(node.orelse, deadcode) + + if else_is_dead and not contains_break(node.body): + # for a in l: + # something() + # else: + # return None + # deadcode() + deadcode = True + + elif isinstance(node, ast.comprehension): + self.walk_deadcode(node.target, deadcode) + self.walk_deadcode(node.iter, deadcode) + + for if_ in node.ifs: + deadcode = self.static_value(if_, deadcode) is False or deadcode + + elif isinstance(node, (ast.ListComp, ast.GeneratorExp, ast.SetComp)): + branch_dead = self.check_stmts(node.generators, deadcode) + self.walk_deadcode(node.elt, branch_dead) + + elif isinstance(node, ast.DictComp): + branch_dead = self.check_stmts(node.generators, deadcode) + self.walk_deadcode(node.key, branch_dead) + self.walk_deadcode(node.value, branch_dead) + + elif isinstance(node, ast.IfExp): + + test_value = self.static_value(node.test, deadcode) + + self.walk_deadcode( + node.body, deadcode or (test_value is False) + ) + + self.walk_deadcode( + node.orelse, deadcode or (test_value is True) + ) + + elif isinstance(node, (ast.While)): + cnd = self.static_value(node.test, deadcode) + + self.check_stmts(node.body, deadcode or cnd is False) + else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) + + if cnd is True and not contains_break(node): + # while True: ... no break + deadcode = True + + if else_is_dead and not contains_break(node.body): + # for a in l: + # something() + # else: + # return None + # deadcode() + deadcode = True + + elif isinstance(node, (ast.Try, ast.TryStar if py11 else ())): + try_dead = self.check_stmts(node.body, deadcode) + + for handler in node.handlers: + handler.deadcode = deadcode + self.walk_deadcode(handler.type, deadcode) + + handlers_dead = all( + [self.check_stmts(h.body, deadcode) for h in node.handlers] + ) + else_dead = self.check_stmts(node.orelse, try_dead) + final_dead = self.check_stmts(node.finalbody, deadcode) + + deadcode = (handlers_dead and else_dead) or final_dead + + elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): + dead_op = deadcode + for v in node.values: + if self.static_value(v, dead_op) is False: + dead_op = True + + elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): + dead_op = deadcode + for v in node.values: + if self.static_value(v, dead_op) is True: + dead_op = True + + elif isinstance(node, ast.Expr): + # dead expressions: + # > 5+5 + # for example + dead_expr = self.has_static_value(node.value) + if ( + isinstance( + node.parent, + (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef), + ) + and node.parent.body[0] == node + and( isinstance(node.value, ast.Constant) + and isinstance(node.value.value, str) if sys.version_info >= (3,6) else isinstance(node.value,ast.Str) ) + ): + # docstring + dead_expr = False + + self.walk_deadcode(node.value, dead_expr or deadcode) + + else: + + for n in ast.iter_child_nodes(node): + self.walk_deadcode(n, deadcode) + + return deadcode + + +def is_deadcode(node): + if hasattr(node,"deadcode"): + return node.deadcode + + module=node + while hasattr(module,"parent"): + module=module.parent + + Deadcode().annotate(module) + + return node.deadcode - visited.add(code) - return False - node._is_deadcode = not contains_sentinel(code) - return node._is_deadcode diff --git a/tests/features/2.7.json b/tests/features/2.7.json new file mode 100644 index 0000000..4e4a8b4 --- /dev/null +++ b/tests/features/2.7.json @@ -0,0 +1,343 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + true + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.10.json b/tests/features/3.10.json new file mode 100644 index 0000000..286b643 --- /dev/null +++ b/tests/features/3.10.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + true + ], + [ + "\\|=", + true + ], + [ + "&=", + true + ], + [ + "^=", + true + ], + [ + "@=", + true + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + true + ], + [ + "__exit__", + true + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.11.json b/tests/features/3.11.json new file mode 100644 index 0000000..84448e8 --- /dev/null +++ b/tests/features/3.11.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + true + ], + [ + "\\|=", + true + ], + [ + "&=", + true + ], + [ + "^=", + true + ], + [ + "@=", + true + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + true + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + true + ], + [ + "__exit__", + true + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.5.json b/tests/features/3.5.json new file mode 100644 index 0000000..a03083c --- /dev/null +++ b/tests/features/3.5.json @@ -0,0 +1,355 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ], + [ + "@=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.6.json b/tests/features/3.6.json new file mode 100644 index 0000000..a84689f --- /dev/null +++ b/tests/features/3.6.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ], + [ + "@=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.7.json b/tests/features/3.7.json new file mode 100644 index 0000000..a84689f --- /dev/null +++ b/tests/features/3.7.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ], + [ + "@=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.8.json b/tests/features/3.8.json new file mode 100644 index 0000000..78baeed --- /dev/null +++ b/tests/features/3.8.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + true + ], + [ + "\\|=", + true + ], + [ + "&=", + true + ], + [ + "^=", + true + ], + [ + "@=", + true + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + true + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/3.9.json b/tests/features/3.9.json new file mode 100644 index 0000000..78baeed --- /dev/null +++ b/tests/features/3.9.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + true + ], + [ + "\\|=", + true + ], + [ + "&=", + true + ], + [ + "^=", + true + ], + [ + "@=", + true + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + true + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/pypy2.7.json b/tests/features/pypy2.7.json new file mode 100644 index 0000000..4e4a8b4 --- /dev/null +++ b/tests/features/pypy2.7.json @@ -0,0 +1,343 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + true + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/pypy3.5.json b/tests/features/pypy3.5.json new file mode 100644 index 0000000..a03083c --- /dev/null +++ b/tests/features/pypy3.5.json @@ -0,0 +1,355 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ], + [ + "@=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/features/pypy3.6.json b/tests/features/pypy3.6.json new file mode 100644 index 0000000..a84689f --- /dev/null +++ b/tests/features/pypy3.6.json @@ -0,0 +1,360 @@ +[ + [ + "test_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_call", + [ + [ + "call", + true + ] + ] + ], + [ + "test_compare_ops", + [ + [ + "<", + true + ], + [ + "<=", + true + ], + [ + ">", + true + ], + [ + ">=", + true + ], + [ + "!=", + true + ], + [ + "==", + true + ], + [ + "in", + true + ], + [ + "not in", + true + ], + [ + "assert 5>=", + false + ], + [ + "\\|=", + false + ], + [ + "&=", + false + ], + [ + "^=", + false + ], + [ + "@=", + false + ] + ] + ], + [ + "test_known_issues", + [ + [ + "same generator", + false + ] + ] + ], + [ + "test_reverse_binary_operators", + [ + [ + "+", + true + ], + [ + "-", + true + ], + [ + "/", + true + ], + [ + "//", + true + ], + [ + "*", + true + ], + [ + "%", + true + ], + [ + "**", + true + ], + [ + "<<", + true + ], + [ + ">>", + true + ], + [ + "\\|", + true + ], + [ + "&", + true + ], + [ + "^", + true + ], + [ + "@", + true + ] + ] + ], + [ + "test_setattr", + [ + [ + "t.attr = 5", + true + ], + [ + "t.attr, g.attr= 5, 5", + true + ], + [ + "setattr(t,\"attr\",5)", + true + ] + ] + ], + [ + "test_setitem", + [ + [ + "obj[index]=value", + true + ], + [ + "obj[start:end]=value", + true + ] + ] + ], + [ + "test_with", + [ + [ + "__enter__", + false + ], + [ + "__exit__", + false + ] + ] + ] +] \ No newline at end of file diff --git a/tests/generate_small_sample.py b/tests/generate_small_sample.py index 2f843d4..55f2312 100644 --- a/tests/generate_small_sample.py +++ b/tests/generate_small_sample.py @@ -18,10 +18,13 @@ from rich.syntax import Syntax from rich.console import Console import argparse +import _pytest.outcomes +import json last_samples_dir = Path(__file__).parent / "last_samples" last_samples_dir.mkdir(exist_ok=True) +checked_files_json=last_samples_dir/"checked.json" small_samples = Path(__file__).parent / "small_samples" @@ -51,7 +54,6 @@ def big_samples(folder): hashes.add(h) yield p - def test_file(filename: Path): code = filename.read_text() @@ -68,7 +70,12 @@ def test_file(filename: Path): with contextlib.redirect_stderr(dev_null): with contextlib.redirect_stdout(dev_null): test.check_filename(filename, check_names=True) - except: + except _pytest.outcomes.Skipped: + return True + except MemoryError: + return True + + except Exception as e: return False return True @@ -99,18 +106,29 @@ def main(): console.print(f"Check files in tests/last_samples and {folder}:") console.print() + checked_files=set() + + if checked_files_json.exists(): + checked_files={Path(file) for file in json.loads(checked_files_json.read_text())} + with Progress() as progress: task_collect = progress.add_task(description="collect files ...", total=None) with get_context("spawn").Pool(maxtasksperchild=100) as p: - files = list( + files = set( progress.track( big_samples(folder), task_id=task_collect, description="collect files...", ) ) + checked_files=checked_files & files + files=files - checked_files + # check the already checked files last + # this improves the propability of finding new issues sooner + files=[*files,*checked_files] + progress.reset(task_collect, description="check files...", total=len(files)) for result, filename in progress.track( @@ -122,7 +140,7 @@ def main(): break_file.unlink() sys.exit(0) - if time.time() > end_time: + if time.time() > end_time and False : print("Timeout") sys.exit(0) @@ -130,6 +148,8 @@ def main(): print(f"{filename} is failing the tests -> minimize\n") failing_code = filename.read_text() break + else: + checked_files.add(filename) else: progress.stop() console.print() @@ -137,12 +157,19 @@ def main(): f" :fireworks: checked {len(files)} files and everything was ok :fireworks:" ) console.print() + checked_files=[] + checked_files_json.write_text(json.dumps([])) return + + + p.terminate() (last_samples_dir / f"{source_hash(failing_code)}.py").write_text(failing_code) + checked_files_json.write_text(json.dumps([str(path) for path in checked_files])) + def check_for_error(source: str): with tempfile.NamedTemporaryFile(delete=False) as tmp_file: tmp_file.write(source.encode("utf8")) diff --git a/tests/sample_results/_parser-py-3.10.json b/tests/sample_results/_parser-py-3.10.json index 5939cc9..4c52ced 100644 --- a/tests/sample_results/_parser-py-3.10.json +++ b/tests/sample_results/_parser-py-3.10.json @@ -1,4 +1,68 @@ [ + [ + "STORE_NAME", + "from __future__ import annotations" + ], + [ + "STORE_NAME", + "from collections.abc import Iterable" + ], + [ + "STORE_NAME", + "import string" + ], + [ + "STORE_NAME", + "from types import MappingProxyType" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], [ "LOAD_NAME", "frozenset" @@ -11,6 +75,10 @@ "CALL_FUNCTION", "range(32)" ], + [ + "CALL_FUNCTION", + "(chr(i) for i in range(32))" + ], [ "CALL_FUNCTION", "frozenset(chr(i) for i in range(32))" @@ -35,6 +103,10 @@ "BINARY_OR", "frozenset(chr(i) for i in range(32)) | frozenset(chr(127))" ], + [ + "STORE_NAME", + "ASCII_CTRL" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -51,6 +123,10 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\")" ], + [ + "STORE_NAME", + "ILLEGAL_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -67,18 +143,34 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\\n\")" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_COMMENT_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -87,6 +179,10 @@ "CALL_FUNCTION", "frozenset(\" \\t\")" ], + [ + "STORE_NAME", + "TOML_WS" + ], [ "LOAD_NAME", "TOML_WS" @@ -103,6 +199,10 @@ "BINARY_OR", "TOML_WS | frozenset(\"\\n\")" ], + [ + "STORE_NAME", + "TOML_WS_AND_NEWLINE" + ], [ "LOAD_NAME", "frozenset" @@ -135,6 +235,10 @@ "CALL_FUNCTION", "frozenset(string.ascii_letters + string.digits + \"-_\")" ], + [ + "STORE_NAME", + "BARE_KEY_CHARS" + ], [ "LOAD_NAME", "BARE_KEY_CHARS" @@ -151,6 +255,10 @@ "BINARY_OR", "BARE_KEY_CHARS | frozenset(\"\\\"'\")" ], + [ + "STORE_NAME", + "KEY_INITIAL_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -167,6 +275,10 @@ "CALL_FUNCTION", "frozenset(string.hexdigits)" ], + [ + "STORE_NAME", + "HEXDIGIT_CHARS" + ], [ "LOAD_NAME", "MappingProxyType" @@ -175,22 +287,166 @@ "CALL_FUNCTION", "MappingProxyType(\n {\n \"\\\\b\": \"\\u0008\", # backspace\n \"\\\\t\": \"\\u0009\", # tab\n \"\\\\n\": \"\\u000A\", # linefeed\n \"\\\\f\": \"\\u000C\", # form feed\n \"\\\\r\": \"\\u000D\", # carriage return\n '\\\\\"': \"\\u0022\", # quote\n \"\\\\\\\\\": \"\\u005C\", # backslash\n }\n)" ], + [ + "STORE_NAME", + "BASIC_STR_ESCAPE_REPLACEMENTS" + ], [ "LOAD_NAME", "ValueError" ], + [ + "CALL_FUNCTION", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def load(fp: BinaryIO, /, *, parse_float: ParseFloat = float) -> dict[str, Any]:\n \"\"\"Parse TOML from a binary file object.\"\"\"\n b = fp.read()\n try:\n s = b.decode()\n except AttributeError:\n raise TypeError(\n \"File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`\"\n ) from None\n return loads(s, parse_float=parse_float)" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa: C901\n \"\"\"Parse TOML from a string.\"\"\"\n\n # The spec allows converting \"\\r\\n\" to \"\\n\", even in string\n # literals. Let's do so to simplify parsing.\n src = s.replace(\"\\r\\n\", \"\\n\")\n pos = 0\n out = Output(NestedDict(), Flags())\n header: Key = ()\n parse_float = make_safe_parse_float(parse_float)\n\n # Parse one statement at a time\n # (typically means one line in TOML source)\n while True:\n # 1. Skip line leading whitespace\n pos = skip_chars(src, pos, TOML_WS)\n\n # 2. Parse rules. Expect one of the following:\n # - end of file\n # - end of line\n # - comment\n # - key/value pair\n # - append dict to list (and move to its namespace)\n # - create dict (and move to its namespace)\n # Skip trailing whitespace when applicable.\n try:\n char = src[pos]\n except IndexError:\n break\n if char == \"\\n\":\n pos += 1\n continue\n if char in KEY_INITIAL_CHARS:\n pos = key_value_rule(src, pos, out, header, parse_float)\n pos = skip_chars(src, pos, TOML_WS)\n elif char == \"[\":\n try:\n second_char: str | None = src[pos + 1]\n except IndexError:\n second_char = None\n out.flags.finalize_pending()\n if second_char == \"[\":\n pos, header = create_list_rule(src, pos, out)\n else:\n pos, header = create_dict_rule(src, pos, out)\n pos = skip_chars(src, pos, TOML_WS)\n elif char != \"#\":\n raise suffixed_err(src, pos, \"Invalid statement\")\n\n # 3. Skip comment\n pos = skip_comment(src, pos)\n\n # 4. Expect end of line or end of file\n try:\n char = src[pos]\n except IndexError:\n break\n if char != \"\\n\":\n raise suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )\n pos += 1\n\n return out.data.dict" + ], + [ + "CALL_FUNCTION", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "CALL_FUNCTION", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], [ "LOAD_NAME", "NamedTuple" ], + [ + "CALL_FUNCTION", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:\n try:\n while src[pos] in chars:\n pos += 1\n except IndexError:\n pass\n return pos" + ], + [ + "STORE_NAME", + "def skip_until(\n src: str,\n pos: Pos,\n expect: str,\n *,\n error_on: frozenset[str],\n error_on_eof: bool,\n) -> Pos:\n try:\n new_pos = src.index(expect, pos)\n except ValueError:\n new_pos = len(src)\n if error_on_eof:\n raise suffixed_err(src, new_pos, f\"Expected {expect!r}\") from None\n\n if not error_on.isdisjoint(src[pos:new_pos]):\n while src[pos] not in error_on:\n pos += 1\n raise suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")\n return new_pos" + ], + [ + "STORE_NAME", + "def skip_comment(src: str, pos: Pos) -> Pos:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char == \"#\":\n return skip_until(\n src, pos + 1, \"\\n\", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False\n )\n return pos" + ], + [ + "STORE_NAME", + "def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:\n while True:\n pos_before_skip = pos\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n pos = skip_comment(src, pos)\n if pos == pos_before_skip:\n return pos" + ], + [ + "STORE_NAME", + "def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 1 # Skip \"[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot declare {key} twice\")\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.get_or_create_nest(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]\", pos):\n raise suffixed_err(src, pos, \"Expected ']' at the end of a table declaration\")\n return pos + 1, key" + ], + [ + "STORE_NAME", + "def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 2 # Skip \"[[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n # Free the namespace now that it points to another empty list item...\n out.flags.unset_all(key)\n # ...but this key precisely is still prohibited from table declaration\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.append_nest_to_list(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]]\", pos):\n raise suffixed_err(src, pos, \"Expected ']]' at the end of an array declaration\")\n return pos + 2, key" + ], + [ + "STORE_NAME", + "def key_value_rule(\n src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat\n) -> Pos:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n abs_key_parent = header + key_parent\n\n relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))\n for cont_key in relative_path_cont_keys:\n # Check that dotted key syntax does not redefine an existing table\n if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):\n raise suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")\n # Containers in the relative path can't be opened with the table syntax or\n # dotted key/value syntax in following table sections.\n out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)\n\n if out.flags.is_(abs_key_parent, Flags.FROZEN):\n raise suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )\n\n try:\n nest = out.data.get_or_create_nest(abs_key_parent)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\")\n # Mark inline table and array namespaces recursively immutable\n if isinstance(value, (dict, list)):\n out.flags.set(header + key, Flags.FROZEN, recursive=True)\n nest[key_stem] = value\n return pos" + ], + [ + "STORE_NAME", + "def parse_key_value_pair(\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Key, Any]:\n pos, key = parse_key(src, pos)\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \"=\":\n raise suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, value = parse_value(src, pos, parse_float)\n return pos, key, value" + ], + [ + "STORE_NAME", + "def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:\n pos, key_part = parse_key_part(src, pos)\n key: Key = (key_part,)\n pos = skip_chars(src, pos, TOML_WS)\n while True:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \".\":\n return pos, key\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, key_part = parse_key_part(src, pos)\n key += (key_part,)\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char in BARE_KEY_CHARS:\n start_pos = pos\n pos = skip_chars(src, pos, BARE_KEY_CHARS)\n return pos, src[start_pos:pos]\n if char == \"'\":\n return parse_literal_str(src, pos)\n if char == '\"':\n return parse_one_line_basic_str(src, pos)\n raise suffixed_err(src, pos, \"Invalid initial character for a key part\")" + ], + [ + "STORE_NAME", + "def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1\n return parse_basic_str(src, pos, multiline=False)" + ], + [ + "STORE_NAME", + "def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]:\n pos += 1\n array: list = []\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array\n while True:\n pos, val = parse_value(src, pos, parse_float)\n array.append(val)\n pos = skip_comments_and_array_ws(src, pos)\n\n c = src[pos : pos + 1]\n if c == \"]\":\n return pos + 1, array\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed array\")\n pos += 1\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array" + ], + [ + "STORE_NAME", + "def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]:\n pos += 1\n nested_dict = NestedDict()\n flags = Flags()\n\n pos = skip_chars(src, pos, TOML_WS)\n if src.startswith(\"}\", pos):\n return pos + 1, nested_dict.dict\n while True:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n if flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n try:\n nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")\n nest[key_stem] = value\n pos = skip_chars(src, pos, TOML_WS)\n c = src[pos : pos + 1]\n if c == \"}\":\n return pos + 1, nested_dict.dict\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed inline table\")\n if isinstance(value, (dict, list)):\n flags.set(key, Flags.FROZEN, recursive=True)\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape(\n src: str, pos: Pos, *, multiline: bool = False\n) -> tuple[Pos, str]:\n escape_id = src[pos : pos + 2]\n pos += 2\n if multiline and escape_id in {\"\\\\ \", \"\\\\\\t\", \"\\\\\\n\"}:\n # Skip whitespace until next non-whitespace character or end of\n # the doc. Error if non-whitespace is found before newline.\n if escape_id != \"\\\\\\n\":\n pos = skip_chars(src, pos, TOML_WS)\n try:\n char = src[pos]\n except IndexError:\n return pos, \"\"\n if char != \"\\n\":\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n return pos, \"\"\n if escape_id == \"\\\\u\":\n return parse_hex_char(src, pos, 4)\n if escape_id == \"\\\\U\":\n return parse_hex_char(src, pos, 8)\n try:\n return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]\n except KeyError:\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\") from None" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:\n return parse_basic_str_escape(src, pos, multiline=True)" + ], + [ + "STORE_NAME", + "def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:\n hex_str = src[pos : pos + hex_len]\n if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):\n raise suffixed_err(src, pos, \"Invalid hex value\")\n pos += hex_len\n hex_int = int(hex_str, 16)\n if not is_unicode_scalar_value(hex_int):\n raise suffixed_err(src, pos, \"Escaped character is not a Unicode scalar value\")\n return pos, chr(hex_int)" + ], + [ + "STORE_NAME", + "def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1 # Skip starting apostrophe\n start_pos = pos\n pos = skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )\n return pos + 1, src[start_pos:pos]" + ], + [ + "STORE_NAME", + "def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:\n pos += 3\n if src.startswith(\"\\n\", pos):\n pos += 1\n\n if literal:\n delim = \"'\"\n end_pos = skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )\n result = src[pos:end_pos]\n pos = end_pos + 3\n else:\n delim = '\"'\n pos, result = parse_basic_str(src, pos, multiline=True)\n\n # Add at maximum two extra apostrophes/quotes if the end sequence\n # is 4 or 5 chars long instead of just 3.\n if not src.startswith(delim, pos):\n return pos, result\n pos += 1\n if not src.startswith(delim, pos):\n return pos, result + delim\n pos += 1\n return pos, result + (delim * 2)" + ], + [ + "STORE_NAME", + "def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:\n if multiline:\n error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape_multiline\n else:\n error_on = ILLEGAL_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape\n result = \"\"\n start_pos = pos\n while True:\n try:\n char = src[pos]\n except IndexError:\n raise suffixed_err(src, pos, \"Unterminated string\") from None\n if char == '\"':\n if not multiline:\n return pos + 1, result + src[start_pos:pos]\n if src.startswith('\"\"\"', pos):\n return pos + 3, result + src[start_pos:pos]\n pos += 1\n continue\n if char == \"\\\\\":\n result += src[start_pos:pos]\n pos, parsed_escape = parse_escapes(src, pos)\n result += parsed_escape\n start_pos = pos\n continue\n if char in error_on:\n raise suffixed_err(src, pos, f\"Illegal character {char!r}\")\n pos += 1" + ], + [ + "STORE_NAME", + "def parse_value( # noqa: C901\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Any]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n\n # IMPORTANT: order conditions based on speed of checking and likelihood\n\n # Basic strings\n if char == '\"':\n if src.startswith('\"\"\"', pos):\n return parse_multiline_str(src, pos, literal=False)\n return parse_one_line_basic_str(src, pos)\n\n # Literal strings\n if char == \"'\":\n if src.startswith(\"'''\", pos):\n return parse_multiline_str(src, pos, literal=True)\n return parse_literal_str(src, pos)\n\n # Booleans\n if char == \"t\":\n if src.startswith(\"true\", pos):\n return pos + 4, True\n if char == \"f\":\n if src.startswith(\"false\", pos):\n return pos + 5, False\n\n # Arrays\n if char == \"[\":\n return parse_array(src, pos, parse_float)\n\n # Inline tables\n if char == \"{\":\n return parse_inline_table(src, pos, parse_float)\n\n # Dates and times\n datetime_match = RE_DATETIME.match(src, pos)\n if datetime_match:\n try:\n datetime_obj = match_to_datetime(datetime_match)\n except ValueError as e:\n raise suffixed_err(src, pos, \"Invalid date or datetime\") from e\n return datetime_match.end(), datetime_obj\n localtime_match = RE_LOCALTIME.match(src, pos)\n if localtime_match:\n return localtime_match.end(), match_to_localtime(localtime_match)\n\n # Integers and \"normal\" floats.\n # The regex will greedily match any type starting with a decimal\n # char, so needs to be located after handling of dates and times.\n number_match = RE_NUMBER.match(src, pos)\n if number_match:\n return number_match.end(), match_to_number(number_match, parse_float)\n\n # Special floats\n first_three = src[pos : pos + 3]\n if first_three in {\"inf\", \"nan\"}:\n return pos + 3, parse_float(first_three)\n first_four = src[pos : pos + 4]\n if first_four in {\"-inf\", \"+inf\", \"-nan\", \"+nan\"}:\n return pos + 4, parse_float(first_four)\n\n raise suffixed_err(src, pos, \"Invalid value\")" + ], + [ + "STORE_NAME", + "def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError:\n \"\"\"Return a `TOMLDecodeError` where error message is suffixed with\n coordinates in source.\"\"\"\n\n def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"\n\n return TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" + ], + [ + "STORE_NAME", + "def is_unicode_scalar_value(codepoint: int) -> bool:\n return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)" + ], + [ + "STORE_NAME", + "def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:\n \"\"\"A decorator to make `parse_float` safe.\n\n `parse_float` must not return dicts or lists, because these types\n would be mixed with parsed TOML tables and arrays, thus confusing\n the parser. The returned decorated callable raises `ValueError`\n instead of returning illegal types.\n \"\"\"\n # The default `float` callable never returns illegal types. Optimize it.\n if parse_float is float: # type: ignore[comparison-overlap]\n return float\n\n def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value\n\n return safe_parse_float" + ], + [ + "LOAD_FAST", + "(chr(i) for i in range(32))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "chr" @@ -203,6 +459,22 @@ "CALL_FUNCTION", "chr(i)" ], + [ + "LOAD_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_FAST", "fp" @@ -215,6 +487,10 @@ "CALL_METHOD", "fp.read()" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "b" @@ -227,6 +503,10 @@ "CALL_METHOD", "b.decode()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -267,6 +547,14 @@ "CALL_METHOD", "s.replace(\"\\r\\n\", \"\\n\")" ], + [ + "STORE_FAST", + "src" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "Output" @@ -291,6 +579,14 @@ "CALL_FUNCTION", "Output(NestedDict(), Flags())" ], + [ + "STORE_FAST", + "out" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "make_safe_parse_float" @@ -303,6 +599,10 @@ "CALL_FUNCTION", "make_safe_parse_float(parse_float)" ], + [ + "STORE_FAST", + "parse_float" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -323,6 +623,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -335,6 +639,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -359,6 +667,14 @@ "COMPARE_OP", "char == \"\\n\"" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -399,6 +715,10 @@ "CALL_FUNCTION", "key_value_rule(src, pos, out, header, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -419,6 +739,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -443,10 +767,18 @@ "BINARY_SUBSCR", "src[pos + 1]" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_FAST", "out" @@ -491,6 +823,14 @@ "CALL_FUNCTION", "create_list_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "create_dict_rule" @@ -511,6 +851,14 @@ "CALL_FUNCTION", "create_dict_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -531,6 +879,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -571,6 +923,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -583,6 +939,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -623,6 +983,62 @@ "CALL_FUNCTION", "suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], + [ + "LOAD_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "\"\"\"Flags that map to parsed keys/namespaces.\"\"\"" + ], + [ + "STORE_NAME", + "FROZEN" + ], + [ + "STORE_NAME", + "EXPLICIT_NEST" + ], + [ + "STORE_NAME", + " def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()" + ], + [ + "STORE_NAME", + " def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))" + ], + [ + "STORE_NAME", + " def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()" + ], + [ + "STORE_NAME", + " def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)" + ], + [ + "STORE_NAME", + " def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)" + ], + [ + "STORE_NAME", + " def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], [ "LOAD_FAST", "self" @@ -679,6 +1095,14 @@ "LOAD_ATTR", "self._pending_flags" ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "flag" + ], [ "LOAD_FAST", "self" @@ -723,6 +1147,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -731,6 +1159,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -759,6 +1191,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "cont" @@ -787,6 +1223,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -803,10 +1243,22 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_parent" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -863,6 +1315,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key_stem" @@ -947,6 +1403,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -955,6 +1415,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -979,6 +1443,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "inner_cont" + ], [ "LOAD_FAST", "flag" @@ -1003,6 +1471,10 @@ "BINARY_SUBSCR", "inner_cont[\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -1011,6 +1483,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_stem" @@ -1035,6 +1511,10 @@ "BINARY_SUBSCR", "cont[key_stem]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "flag" @@ -1067,6 +1547,30 @@ "CONTAINS_OP", "flag in cont[\"recursive_flags\"]" ], + [ + "LOAD_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + " def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}" + ], + [ + "STORE_NAME", + " def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont" + ], + [ + "STORE_NAME", + " def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], [ "LOAD_FAST", "self" @@ -1083,10 +1587,18 @@ "LOAD_ATTR", "self.dict" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -1123,6 +1635,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "access_lists" @@ -1151,6 +1667,10 @@ "BINARY_SUBSCR", "cont[-1]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1199,6 +1719,10 @@ "CALL_METHOD", "self.get_or_create_nest(key[:-1])" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -1207,6 +1731,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "last_key" + ], [ "LOAD_FAST", "last_key" @@ -1231,6 +1759,10 @@ "BINARY_SUBSCR", "cont[last_key]" ], + [ + "STORE_FAST", + "list_" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1279,6 +1811,34 @@ "STORE_SUBSCR", "cont[last_key]" ], + [ + "LOAD_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "LOAD_NAME", + "data: NestedDict" + ], + [ + "STORE_SUBSCR", + "data: NestedDict" + ], + [ + "LOAD_NAME", + "flags: Flags" + ], + [ + "STORE_SUBSCR", + "flags: Flags" + ], [ "LOAD_FAST", "src" @@ -1299,6 +1859,14 @@ "CONTAINS_OP", "src[pos] in chars" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -1355,6 +1923,10 @@ "CALL_METHOD", "src.index(expect, pos)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_GLOBAL", "ValueError" @@ -1371,6 +1943,10 @@ "CALL_FUNCTION", "len(src)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_FAST", "error_on_eof" @@ -1391,6 +1967,10 @@ "LOAD_FAST", "expect" ], + [ + "BUILD_STRING", + "f\"Expected {expect!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, new_pos, f\"Expected {expect!r}\")" @@ -1443,6 +2023,14 @@ "CONTAINS_OP", "src[pos] not in error_on" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -1487,6 +2075,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "BUILD_STRING", + "f\"Found invalid character {src[pos]!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")" @@ -1507,10 +2099,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -1551,6 +2151,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos_before_skip" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1571,6 +2175,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_comment" @@ -1587,6 +2195,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -1603,6 +2215,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1623,6 +2243,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1639,6 +2263,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1711,6 +2343,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot declare {key} twice\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot declare {key} twice\")" @@ -1827,6 +2463,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1847,6 +2491,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1863,6 +2511,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1907,6 +2563,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -2063,6 +2723,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_DEREF", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_DEREF", "key" @@ -2079,6 +2751,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_DEREF", "header" @@ -2091,6 +2771,10 @@ "BINARY_ADD", "header + key_parent" ], + [ + "STORE_FAST", + "abs_key_parent" + ], [ "LOAD_GLOBAL", "range" @@ -2111,10 +2795,22 @@ "CALL_FUNCTION", "range(1, len(key))" ], + [ + "CALL_FUNCTION", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "relative_path_cont_keys" + ], [ "LOAD_FAST", "relative_path_cont_keys" ], + [ + "STORE_FAST", + "cont_key" + ], [ "LOAD_FAST", "out" @@ -2159,6 +2855,10 @@ "LOAD_FAST", "cont_key" ], + [ + "BUILD_STRING", + "f\"Cannot redefine namespace {cont_key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")" @@ -2235,6 +2935,10 @@ "LOAD_FAST", "abs_key_parent" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {abs_key_parent}\"" + ], [ "CALL_FUNCTION", "suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )" @@ -2259,6 +2963,10 @@ "CALL_METHOD", "out.data.get_or_create_nest(abs_key_parent)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -2383,6 +3091,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_DEREF", "header" @@ -2419,6 +3135,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "src" @@ -2431,10 +3155,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2459,6 +3191,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2479,6 +3219,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_value" @@ -2499,6 +3243,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "pos" @@ -2527,10 +3279,22 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2551,6 +3315,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2563,10 +3331,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2583,6 +3359,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2603,6 +3387,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key_part" @@ -2619,10 +3407,26 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], + [ + "LOAD_FAST", + "key" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2643,6 +3447,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2655,10 +3463,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2675,6 +3491,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2695,6 +3515,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, BARE_KEY_CHARS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -2779,6 +3603,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid initial character for a key part\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -2795,6 +3627,18 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=False)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "array" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2811,6 +3655,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2859,6 +3707,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "val" + ], [ "LOAD_FAST", "array" @@ -2891,6 +3747,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2911,6 +3771,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -2955,6 +3819,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unclosed array\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2971,6 +3843,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2999,6 +3875,14 @@ "LOAD_FAST", "array" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "NestedDict" @@ -3007,6 +3891,10 @@ "CALL_FUNCTION", "NestedDict()" ], + [ + "STORE_FAST", + "nested_dict" + ], [ "LOAD_GLOBAL", "Flags" @@ -3015,6 +3903,10 @@ "CALL_FUNCTION", "Flags()" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3035,6 +3927,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3087,6 +3983,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "key" @@ -3103,6 +4011,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "flags" @@ -3143,6 +4059,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -3163,6 +4083,10 @@ "CALL_FUNCTION_KW", "nested_dict.get_or_create_nest(key_parent, access_lists=False)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -3211,6 +4135,10 @@ "LOAD_FAST", "key_stem" ], + [ + "BUILD_STRING", + "f\"Duplicate inline table key {key_stem!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")" @@ -3251,6 +4179,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3271,6 +4203,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -3363,6 +4299,14 @@ "CALL_FUNCTION_KW", "flags.set(key, Flags.FROZEN, recursive=True)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3383,6 +4327,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3403,6 +4351,18 @@ "BINARY_SUBSCR", "src[pos : pos + 2]" ], + [ + "STORE_FAST", + "escape_id" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "multiline" @@ -3443,6 +4403,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3455,6 +4419,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -3487,6 +4455,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3507,6 +4483,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3635,6 +4615,10 @@ "BINARY_SUBSCR", "src[pos : pos + hex_len]" ], + [ + "STORE_FAST", + "hex_str" + ], [ "LOAD_GLOBAL", "len" @@ -3687,10 +4671,18 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid hex value\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "hex_len" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -3703,6 +4695,10 @@ "CALL_FUNCTION", "int(hex_str, 16)" ], + [ + "STORE_FAST", + "hex_int" + ], [ "LOAD_GLOBAL", "is_unicode_scalar_value" @@ -3751,6 +4747,18 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3771,6 +4779,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3795,6 +4807,14 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3811,10 +4831,22 @@ "CALL_METHOD", "src.startswith(\"\\n\", pos)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "literal" ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3835,6 +4867,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )" ], + [ + "STORE_FAST", + "end_pos" + ], [ "LOAD_FAST", "src" @@ -3851,6 +4887,10 @@ "BINARY_SUBSCR", "src[pos:end_pos]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "end_pos" @@ -3859,6 +4899,14 @@ "BINARY_ADD", "end_pos + 3" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -3875,6 +4923,14 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=True)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "src" @@ -3903,6 +4959,14 @@ "LOAD_FAST", "result" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3943,6 +5007,14 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos" + ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "result" @@ -3967,22 +5039,46 @@ "LOAD_GLOBAL", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape_multiline" ], + [ + "STORE_FAST", + "parse_escapes" + ], [ "LOAD_GLOBAL", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape" ], + [ + "STORE_FAST", + "parse_escapes" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "src" @@ -3995,6 +5091,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -4107,6 +5207,14 @@ "BINARY_ADD", "result + src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -4115,6 +5223,10 @@ "COMPARE_OP", "char == \"\\\\\"" ], + [ + "LOAD_FAST", + "result" + ], [ "LOAD_FAST", "src" @@ -4131,6 +5243,10 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "parse_escapes" @@ -4147,14 +5263,34 @@ "CALL_FUNCTION", "parse_escapes(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "parsed_escape" + ], + [ + "LOAD_FAST", + "result" + ], [ "LOAD_FAST", "parsed_escape" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "char" @@ -4183,10 +5319,22 @@ "LOAD_FAST", "char" ], + [ + "BUILD_STRING", + "f\"Illegal character {char!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Illegal character {char!r}\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -4199,10 +5347,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -4455,6 +5611,10 @@ "CALL_METHOD", "RE_DATETIME.match(src, pos)" ], + [ + "STORE_FAST", + "datetime_match" + ], [ "LOAD_FAST", "datetime_match" @@ -4471,6 +5631,10 @@ "CALL_FUNCTION", "match_to_datetime(datetime_match)" ], + [ + "STORE_FAST", + "datetime_obj" + ], [ "LOAD_GLOBAL", "ValueError" @@ -4531,6 +5695,10 @@ "CALL_METHOD", "RE_LOCALTIME.match(src, pos)" ], + [ + "STORE_FAST", + "localtime_match" + ], [ "LOAD_FAST", "localtime_match" @@ -4579,6 +5747,10 @@ "CALL_METHOD", "RE_NUMBER.match(src, pos)" ], + [ + "STORE_FAST", + "number_match" + ], [ "LOAD_FAST", "number_match" @@ -4631,6 +5803,10 @@ "BINARY_SUBSCR", "src[pos : pos + 3]" ], + [ + "STORE_FAST", + "first_three" + ], [ "LOAD_FAST", "first_three" @@ -4679,6 +5855,10 @@ "BINARY_SUBSCR", "src[pos : pos + 4]" ], + [ + "STORE_FAST", + "first_four" + ], [ "LOAD_FAST", "first_four" @@ -4723,6 +5903,10 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid value\")" ], + [ + "STORE_FAST", + " def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"" + ], [ "LOAD_GLOBAL", "TOMLDecodeError" @@ -4747,6 +5931,10 @@ "CALL_FUNCTION", "coord_repr(src, pos)" ], + [ + "BUILD_STRING", + "f\"{msg} (at {coord_repr(src, pos)})\"" + ], [ "CALL_FUNCTION", "TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" @@ -4791,6 +5979,10 @@ "BINARY_ADD", "src.count(\"\\n\", 0, pos) + 1" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "line" @@ -4807,6 +5999,10 @@ "BINARY_ADD", "pos + 1" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "pos" @@ -4831,6 +6027,10 @@ "BINARY_SUBTRACT", "pos - src.rindex(\"\\n\", 0, pos)" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "line" @@ -4839,14 +6039,34 @@ "LOAD_FAST", "column" ], + [ + "BUILD_STRING", + "f\"line {line}, column {column}\"" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], [ "LOAD_DEREF", "parse_float" @@ -4863,6 +6083,10 @@ "LOAD_GLOBAL", "float" ], + [ + "STORE_FAST", + " def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value" + ], [ "LOAD_FAST", "safe_parse_float" @@ -4879,6 +6103,10 @@ "CALL_FUNCTION", "parse_float(float_str)" ], + [ + "STORE_FAST", + "float_value" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/_parser-py-3.8.json b/tests/sample_results/_parser-py-3.8.json index eb0e4dd..3535a1b 100644 --- a/tests/sample_results/_parser-py-3.8.json +++ b/tests/sample_results/_parser-py-3.8.json @@ -1,4 +1,68 @@ [ + [ + "STORE_NAME", + "from __future__ import annotations" + ], + [ + "STORE_NAME", + "from collections.abc import Iterable" + ], + [ + "STORE_NAME", + "import string" + ], + [ + "STORE_NAME", + "from types import MappingProxyType" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], [ "LOAD_NAME", "frozenset" @@ -11,6 +75,10 @@ "CALL_FUNCTION", "range(32)" ], + [ + "CALL_FUNCTION", + "(chr(i) for i in range(32))" + ], [ "CALL_FUNCTION", "frozenset(chr(i) for i in range(32))" @@ -35,6 +103,10 @@ "BINARY_OR", "frozenset(chr(i) for i in range(32)) | frozenset(chr(127))" ], + [ + "STORE_NAME", + "ASCII_CTRL" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -51,6 +123,10 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\")" ], + [ + "STORE_NAME", + "ILLEGAL_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -67,18 +143,34 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\\n\")" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_COMMENT_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -87,6 +179,10 @@ "CALL_FUNCTION", "frozenset(\" \\t\")" ], + [ + "STORE_NAME", + "TOML_WS" + ], [ "LOAD_NAME", "TOML_WS" @@ -103,6 +199,10 @@ "BINARY_OR", "TOML_WS | frozenset(\"\\n\")" ], + [ + "STORE_NAME", + "TOML_WS_AND_NEWLINE" + ], [ "LOAD_NAME", "frozenset" @@ -135,6 +235,10 @@ "CALL_FUNCTION", "frozenset(string.ascii_letters + string.digits + \"-_\")" ], + [ + "STORE_NAME", + "BARE_KEY_CHARS" + ], [ "LOAD_NAME", "BARE_KEY_CHARS" @@ -151,6 +255,10 @@ "BINARY_OR", "BARE_KEY_CHARS | frozenset(\"\\\"'\")" ], + [ + "STORE_NAME", + "KEY_INITIAL_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -167,6 +275,10 @@ "CALL_FUNCTION", "frozenset(string.hexdigits)" ], + [ + "STORE_NAME", + "HEXDIGIT_CHARS" + ], [ "LOAD_NAME", "MappingProxyType" @@ -175,22 +287,166 @@ "CALL_FUNCTION", "MappingProxyType(\n {\n \"\\\\b\": \"\\u0008\", # backspace\n \"\\\\t\": \"\\u0009\", # tab\n \"\\\\n\": \"\\u000A\", # linefeed\n \"\\\\f\": \"\\u000C\", # form feed\n \"\\\\r\": \"\\u000D\", # carriage return\n '\\\\\"': \"\\u0022\", # quote\n \"\\\\\\\\\": \"\\u005C\", # backslash\n }\n)" ], + [ + "STORE_NAME", + "BASIC_STR_ESCAPE_REPLACEMENTS" + ], [ "LOAD_NAME", "ValueError" ], + [ + "CALL_FUNCTION", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def load(fp: BinaryIO, /, *, parse_float: ParseFloat = float) -> dict[str, Any]:\n \"\"\"Parse TOML from a binary file object.\"\"\"\n b = fp.read()\n try:\n s = b.decode()\n except AttributeError:\n raise TypeError(\n \"File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`\"\n ) from None\n return loads(s, parse_float=parse_float)" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa: C901\n \"\"\"Parse TOML from a string.\"\"\"\n\n # The spec allows converting \"\\r\\n\" to \"\\n\", even in string\n # literals. Let's do so to simplify parsing.\n src = s.replace(\"\\r\\n\", \"\\n\")\n pos = 0\n out = Output(NestedDict(), Flags())\n header: Key = ()\n parse_float = make_safe_parse_float(parse_float)\n\n # Parse one statement at a time\n # (typically means one line in TOML source)\n while True:\n # 1. Skip line leading whitespace\n pos = skip_chars(src, pos, TOML_WS)\n\n # 2. Parse rules. Expect one of the following:\n # - end of file\n # - end of line\n # - comment\n # - key/value pair\n # - append dict to list (and move to its namespace)\n # - create dict (and move to its namespace)\n # Skip trailing whitespace when applicable.\n try:\n char = src[pos]\n except IndexError:\n break\n if char == \"\\n\":\n pos += 1\n continue\n if char in KEY_INITIAL_CHARS:\n pos = key_value_rule(src, pos, out, header, parse_float)\n pos = skip_chars(src, pos, TOML_WS)\n elif char == \"[\":\n try:\n second_char: str | None = src[pos + 1]\n except IndexError:\n second_char = None\n out.flags.finalize_pending()\n if second_char == \"[\":\n pos, header = create_list_rule(src, pos, out)\n else:\n pos, header = create_dict_rule(src, pos, out)\n pos = skip_chars(src, pos, TOML_WS)\n elif char != \"#\":\n raise suffixed_err(src, pos, \"Invalid statement\")\n\n # 3. Skip comment\n pos = skip_comment(src, pos)\n\n # 4. Expect end of line or end of file\n try:\n char = src[pos]\n except IndexError:\n break\n if char != \"\\n\":\n raise suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )\n pos += 1\n\n return out.data.dict" + ], + [ + "CALL_FUNCTION", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "CALL_FUNCTION", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], [ "LOAD_NAME", "NamedTuple" ], + [ + "CALL_FUNCTION", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:\n try:\n while src[pos] in chars:\n pos += 1\n except IndexError:\n pass\n return pos" + ], + [ + "STORE_NAME", + "def skip_until(\n src: str,\n pos: Pos,\n expect: str,\n *,\n error_on: frozenset[str],\n error_on_eof: bool,\n) -> Pos:\n try:\n new_pos = src.index(expect, pos)\n except ValueError:\n new_pos = len(src)\n if error_on_eof:\n raise suffixed_err(src, new_pos, f\"Expected {expect!r}\") from None\n\n if not error_on.isdisjoint(src[pos:new_pos]):\n while src[pos] not in error_on:\n pos += 1\n raise suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")\n return new_pos" + ], + [ + "STORE_NAME", + "def skip_comment(src: str, pos: Pos) -> Pos:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char == \"#\":\n return skip_until(\n src, pos + 1, \"\\n\", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False\n )\n return pos" + ], + [ + "STORE_NAME", + "def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:\n while True:\n pos_before_skip = pos\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n pos = skip_comment(src, pos)\n if pos == pos_before_skip:\n return pos" + ], + [ + "STORE_NAME", + "def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 1 # Skip \"[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot declare {key} twice\")\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.get_or_create_nest(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]\", pos):\n raise suffixed_err(src, pos, \"Expected ']' at the end of a table declaration\")\n return pos + 1, key" + ], + [ + "STORE_NAME", + "def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 2 # Skip \"[[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n # Free the namespace now that it points to another empty list item...\n out.flags.unset_all(key)\n # ...but this key precisely is still prohibited from table declaration\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.append_nest_to_list(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]]\", pos):\n raise suffixed_err(src, pos, \"Expected ']]' at the end of an array declaration\")\n return pos + 2, key" + ], + [ + "STORE_NAME", + "def key_value_rule(\n src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat\n) -> Pos:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n abs_key_parent = header + key_parent\n\n relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))\n for cont_key in relative_path_cont_keys:\n # Check that dotted key syntax does not redefine an existing table\n if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):\n raise suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")\n # Containers in the relative path can't be opened with the table syntax or\n # dotted key/value syntax in following table sections.\n out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)\n\n if out.flags.is_(abs_key_parent, Flags.FROZEN):\n raise suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )\n\n try:\n nest = out.data.get_or_create_nest(abs_key_parent)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\")\n # Mark inline table and array namespaces recursively immutable\n if isinstance(value, (dict, list)):\n out.flags.set(header + key, Flags.FROZEN, recursive=True)\n nest[key_stem] = value\n return pos" + ], + [ + "STORE_NAME", + "def parse_key_value_pair(\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Key, Any]:\n pos, key = parse_key(src, pos)\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \"=\":\n raise suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, value = parse_value(src, pos, parse_float)\n return pos, key, value" + ], + [ + "STORE_NAME", + "def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:\n pos, key_part = parse_key_part(src, pos)\n key: Key = (key_part,)\n pos = skip_chars(src, pos, TOML_WS)\n while True:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \".\":\n return pos, key\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, key_part = parse_key_part(src, pos)\n key += (key_part,)\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char in BARE_KEY_CHARS:\n start_pos = pos\n pos = skip_chars(src, pos, BARE_KEY_CHARS)\n return pos, src[start_pos:pos]\n if char == \"'\":\n return parse_literal_str(src, pos)\n if char == '\"':\n return parse_one_line_basic_str(src, pos)\n raise suffixed_err(src, pos, \"Invalid initial character for a key part\")" + ], + [ + "STORE_NAME", + "def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1\n return parse_basic_str(src, pos, multiline=False)" + ], + [ + "STORE_NAME", + "def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]:\n pos += 1\n array: list = []\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array\n while True:\n pos, val = parse_value(src, pos, parse_float)\n array.append(val)\n pos = skip_comments_and_array_ws(src, pos)\n\n c = src[pos : pos + 1]\n if c == \"]\":\n return pos + 1, array\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed array\")\n pos += 1\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array" + ], + [ + "STORE_NAME", + "def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]:\n pos += 1\n nested_dict = NestedDict()\n flags = Flags()\n\n pos = skip_chars(src, pos, TOML_WS)\n if src.startswith(\"}\", pos):\n return pos + 1, nested_dict.dict\n while True:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n if flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n try:\n nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")\n nest[key_stem] = value\n pos = skip_chars(src, pos, TOML_WS)\n c = src[pos : pos + 1]\n if c == \"}\":\n return pos + 1, nested_dict.dict\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed inline table\")\n if isinstance(value, (dict, list)):\n flags.set(key, Flags.FROZEN, recursive=True)\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape(\n src: str, pos: Pos, *, multiline: bool = False\n) -> tuple[Pos, str]:\n escape_id = src[pos : pos + 2]\n pos += 2\n if multiline and escape_id in {\"\\\\ \", \"\\\\\\t\", \"\\\\\\n\"}:\n # Skip whitespace until next non-whitespace character or end of\n # the doc. Error if non-whitespace is found before newline.\n if escape_id != \"\\\\\\n\":\n pos = skip_chars(src, pos, TOML_WS)\n try:\n char = src[pos]\n except IndexError:\n return pos, \"\"\n if char != \"\\n\":\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n return pos, \"\"\n if escape_id == \"\\\\u\":\n return parse_hex_char(src, pos, 4)\n if escape_id == \"\\\\U\":\n return parse_hex_char(src, pos, 8)\n try:\n return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]\n except KeyError:\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\") from None" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:\n return parse_basic_str_escape(src, pos, multiline=True)" + ], + [ + "STORE_NAME", + "def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:\n hex_str = src[pos : pos + hex_len]\n if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):\n raise suffixed_err(src, pos, \"Invalid hex value\")\n pos += hex_len\n hex_int = int(hex_str, 16)\n if not is_unicode_scalar_value(hex_int):\n raise suffixed_err(src, pos, \"Escaped character is not a Unicode scalar value\")\n return pos, chr(hex_int)" + ], + [ + "STORE_NAME", + "def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1 # Skip starting apostrophe\n start_pos = pos\n pos = skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )\n return pos + 1, src[start_pos:pos]" + ], + [ + "STORE_NAME", + "def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:\n pos += 3\n if src.startswith(\"\\n\", pos):\n pos += 1\n\n if literal:\n delim = \"'\"\n end_pos = skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )\n result = src[pos:end_pos]\n pos = end_pos + 3\n else:\n delim = '\"'\n pos, result = parse_basic_str(src, pos, multiline=True)\n\n # Add at maximum two extra apostrophes/quotes if the end sequence\n # is 4 or 5 chars long instead of just 3.\n if not src.startswith(delim, pos):\n return pos, result\n pos += 1\n if not src.startswith(delim, pos):\n return pos, result + delim\n pos += 1\n return pos, result + (delim * 2)" + ], + [ + "STORE_NAME", + "def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:\n if multiline:\n error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape_multiline\n else:\n error_on = ILLEGAL_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape\n result = \"\"\n start_pos = pos\n while True:\n try:\n char = src[pos]\n except IndexError:\n raise suffixed_err(src, pos, \"Unterminated string\") from None\n if char == '\"':\n if not multiline:\n return pos + 1, result + src[start_pos:pos]\n if src.startswith('\"\"\"', pos):\n return pos + 3, result + src[start_pos:pos]\n pos += 1\n continue\n if char == \"\\\\\":\n result += src[start_pos:pos]\n pos, parsed_escape = parse_escapes(src, pos)\n result += parsed_escape\n start_pos = pos\n continue\n if char in error_on:\n raise suffixed_err(src, pos, f\"Illegal character {char!r}\")\n pos += 1" + ], + [ + "STORE_NAME", + "def parse_value( # noqa: C901\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Any]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n\n # IMPORTANT: order conditions based on speed of checking and likelihood\n\n # Basic strings\n if char == '\"':\n if src.startswith('\"\"\"', pos):\n return parse_multiline_str(src, pos, literal=False)\n return parse_one_line_basic_str(src, pos)\n\n # Literal strings\n if char == \"'\":\n if src.startswith(\"'''\", pos):\n return parse_multiline_str(src, pos, literal=True)\n return parse_literal_str(src, pos)\n\n # Booleans\n if char == \"t\":\n if src.startswith(\"true\", pos):\n return pos + 4, True\n if char == \"f\":\n if src.startswith(\"false\", pos):\n return pos + 5, False\n\n # Arrays\n if char == \"[\":\n return parse_array(src, pos, parse_float)\n\n # Inline tables\n if char == \"{\":\n return parse_inline_table(src, pos, parse_float)\n\n # Dates and times\n datetime_match = RE_DATETIME.match(src, pos)\n if datetime_match:\n try:\n datetime_obj = match_to_datetime(datetime_match)\n except ValueError as e:\n raise suffixed_err(src, pos, \"Invalid date or datetime\") from e\n return datetime_match.end(), datetime_obj\n localtime_match = RE_LOCALTIME.match(src, pos)\n if localtime_match:\n return localtime_match.end(), match_to_localtime(localtime_match)\n\n # Integers and \"normal\" floats.\n # The regex will greedily match any type starting with a decimal\n # char, so needs to be located after handling of dates and times.\n number_match = RE_NUMBER.match(src, pos)\n if number_match:\n return number_match.end(), match_to_number(number_match, parse_float)\n\n # Special floats\n first_three = src[pos : pos + 3]\n if first_three in {\"inf\", \"nan\"}:\n return pos + 3, parse_float(first_three)\n first_four = src[pos : pos + 4]\n if first_four in {\"-inf\", \"+inf\", \"-nan\", \"+nan\"}:\n return pos + 4, parse_float(first_four)\n\n raise suffixed_err(src, pos, \"Invalid value\")" + ], + [ + "STORE_NAME", + "def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError:\n \"\"\"Return a `TOMLDecodeError` where error message is suffixed with\n coordinates in source.\"\"\"\n\n def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"\n\n return TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" + ], + [ + "STORE_NAME", + "def is_unicode_scalar_value(codepoint: int) -> bool:\n return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)" + ], + [ + "STORE_NAME", + "def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:\n \"\"\"A decorator to make `parse_float` safe.\n\n `parse_float` must not return dicts or lists, because these types\n would be mixed with parsed TOML tables and arrays, thus confusing\n the parser. The returned decorated callable raises `ValueError`\n instead of returning illegal types.\n \"\"\"\n # The default `float` callable never returns illegal types. Optimize it.\n if parse_float is float: # type: ignore[comparison-overlap]\n return float\n\n def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value\n\n return safe_parse_float" + ], + [ + "LOAD_FAST", + "(chr(i) for i in range(32))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "chr" @@ -203,6 +459,22 @@ "CALL_FUNCTION", "chr(i)" ], + [ + "LOAD_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_FAST", "fp" @@ -215,6 +487,10 @@ "CALL_METHOD", "fp.read()" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "b" @@ -227,6 +503,10 @@ "CALL_METHOD", "b.decode()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -267,6 +547,14 @@ "CALL_METHOD", "s.replace(\"\\r\\n\", \"\\n\")" ], + [ + "STORE_FAST", + "src" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "Output" @@ -291,6 +579,14 @@ "CALL_FUNCTION", "Output(NestedDict(), Flags())" ], + [ + "STORE_FAST", + "out" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "make_safe_parse_float" @@ -303,6 +599,10 @@ "CALL_FUNCTION", "make_safe_parse_float(parse_float)" ], + [ + "STORE_FAST", + "parse_float" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -323,6 +623,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -335,6 +639,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -347,6 +655,14 @@ "COMPARE_OP", "char == \"\\n\"" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "char" @@ -387,6 +703,10 @@ "CALL_FUNCTION", "key_value_rule(src, pos, out, header, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -407,6 +727,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -431,10 +755,18 @@ "BINARY_SUBSCR", "src[pos + 1]" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_FAST", "out" @@ -479,6 +811,14 @@ "CALL_FUNCTION", "create_list_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "create_dict_rule" @@ -499,6 +839,14 @@ "CALL_FUNCTION", "create_dict_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -519,6 +867,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -559,6 +911,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -571,6 +927,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -599,6 +959,14 @@ "CALL_FUNCTION", "suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "out" @@ -611,6 +979,54 @@ "LOAD_ATTR", "out.data.dict" ], + [ + "LOAD_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "\"\"\"Flags that map to parsed keys/namespaces.\"\"\"" + ], + [ + "STORE_NAME", + "FROZEN" + ], + [ + "STORE_NAME", + "EXPLICIT_NEST" + ], + [ + "STORE_NAME", + " def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()" + ], + [ + "STORE_NAME", + " def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))" + ], + [ + "STORE_NAME", + " def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()" + ], + [ + "STORE_NAME", + " def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)" + ], + [ + "STORE_NAME", + " def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)" + ], + [ + "STORE_NAME", + " def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], [ "LOAD_FAST", "self" @@ -667,6 +1083,14 @@ "LOAD_ATTR", "self._pending_flags" ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "flag" + ], [ "LOAD_FAST", "self" @@ -711,6 +1135,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -719,6 +1147,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -747,6 +1179,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "cont" @@ -775,6 +1211,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -791,10 +1231,22 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_parent" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -851,6 +1303,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key_stem" @@ -935,6 +1391,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -943,6 +1403,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -967,6 +1431,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "inner_cont" + ], [ "LOAD_FAST", "flag" @@ -991,6 +1459,10 @@ "BINARY_SUBSCR", "inner_cont[\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -999,6 +1471,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_stem" @@ -1023,6 +1499,10 @@ "BINARY_SUBSCR", "cont[key_stem]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "flag" @@ -1036,24 +1516,48 @@ "cont[\"flags\"]" ], [ - "COMPARE_OP", - "flag in cont[\"flags\"]" + "COMPARE_OP", + "flag in cont[\"flags\"]" + ], + [ + "LOAD_FAST", + "flag" + ], + [ + "LOAD_FAST", + "cont" + ], + [ + "BINARY_SUBSCR", + "cont[\"recursive_flags\"]" + ], + [ + "COMPARE_OP", + "flag in cont[\"recursive_flags\"]" + ], + [ + "LOAD_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" ], [ - "LOAD_FAST", - "flag" + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" ], [ - "LOAD_FAST", - "cont" + "STORE_NAME", + " def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}" ], [ - "BINARY_SUBSCR", - "cont[\"recursive_flags\"]" + "STORE_NAME", + " def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont" ], [ - "COMPARE_OP", - "flag in cont[\"recursive_flags\"]" + "STORE_NAME", + " def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" ], [ "LOAD_FAST", @@ -1071,10 +1575,18 @@ "LOAD_ATTR", "self.dict" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -1111,6 +1623,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "access_lists" @@ -1139,6 +1655,10 @@ "BINARY_SUBSCR", "cont[-1]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1187,6 +1707,10 @@ "CALL_METHOD", "self.get_or_create_nest(key[:-1])" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -1195,6 +1719,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "last_key" + ], [ "LOAD_FAST", "last_key" @@ -1219,6 +1747,10 @@ "BINARY_SUBSCR", "cont[last_key]" ], + [ + "STORE_FAST", + "list_" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1267,6 +1799,34 @@ "STORE_SUBSCR", "cont[last_key]" ], + [ + "LOAD_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "LOAD_NAME", + "data: NestedDict" + ], + [ + "STORE_SUBSCR", + "data: NestedDict" + ], + [ + "LOAD_NAME", + "flags: Flags" + ], + [ + "STORE_SUBSCR", + "flags: Flags" + ], [ "LOAD_FAST", "src" @@ -1287,6 +1847,14 @@ "COMPARE_OP", "src[pos] in chars" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "IndexError" @@ -1315,6 +1883,10 @@ "CALL_METHOD", "src.index(expect, pos)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_GLOBAL", "ValueError" @@ -1331,6 +1903,10 @@ "CALL_FUNCTION", "len(src)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_FAST", "error_on_eof" @@ -1351,6 +1927,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Expected {expect!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, new_pos, f\"Expected {expect!r}\")" @@ -1403,6 +1983,14 @@ "COMPARE_OP", "src[pos] not in error_on" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "suffixed_err" @@ -1427,6 +2015,10 @@ "BINARY_SUBSCR", "" ], + [ + "BUILD_STRING", + "f\"Found invalid character {src[pos]!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")" @@ -1447,10 +2039,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -1491,6 +2091,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos_before_skip" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1511,6 +2115,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_comment" @@ -1527,6 +2135,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -1543,6 +2155,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1563,6 +2183,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1579,6 +2203,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1651,6 +2283,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Cannot declare {key} twice\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot declare {key} twice\")" @@ -1767,6 +2403,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "pos += 2" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1787,6 +2431,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1803,6 +2451,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1847,6 +2503,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -2003,6 +2663,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_DEREF", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_DEREF", "key" @@ -2019,6 +2691,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_DEREF", "header" @@ -2031,6 +2711,10 @@ "BINARY_ADD", "header + key_parent" ], + [ + "STORE_FAST", + "abs_key_parent" + ], [ "LOAD_GLOBAL", "range" @@ -2051,10 +2735,22 @@ "CALL_FUNCTION", "range(1, len(key))" ], + [ + "CALL_FUNCTION", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "relative_path_cont_keys" + ], [ "LOAD_FAST", "relative_path_cont_keys" ], + [ + "STORE_FAST", + "cont_key" + ], [ "LOAD_FAST", "out" @@ -2099,6 +2795,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Cannot redefine namespace {cont_key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")" @@ -2175,6 +2875,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {abs_key_parent}\"" + ], [ "CALL_FUNCTION", "suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )" @@ -2199,6 +2903,10 @@ "CALL_METHOD", "out.data.get_or_create_nest(abs_key_parent)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -2323,6 +3031,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_DEREF", "header" @@ -2359,6 +3075,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "src" @@ -2371,10 +3095,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2399,6 +3131,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2419,6 +3159,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_value" @@ -2439,6 +3183,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "pos" @@ -2467,10 +3219,22 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2491,6 +3255,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2503,10 +3271,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2523,6 +3299,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2543,6 +3327,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key_part" @@ -2559,10 +3347,26 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], + [ + "LOAD_FAST", + "key += (key_part,)" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key += (key_part,)" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2583,6 +3387,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2595,10 +3403,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2615,6 +3431,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2635,6 +3455,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, BARE_KEY_CHARS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -2719,6 +3543,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid initial character for a key part\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -2735,6 +3567,18 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=False)" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "array" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2751,6 +3595,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2799,6 +3647,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "val" + ], [ "LOAD_FAST", "array" @@ -2831,6 +3687,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2851,6 +3711,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -2895,6 +3759,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unclosed array\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2911,6 +3783,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2939,6 +3815,14 @@ "LOAD_FAST", "array" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "NestedDict" @@ -2947,6 +3831,10 @@ "CALL_FUNCTION", "NestedDict()" ], + [ + "STORE_FAST", + "nested_dict" + ], [ "LOAD_GLOBAL", "Flags" @@ -2955,6 +3843,10 @@ "CALL_FUNCTION", "Flags()" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2975,6 +3867,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3027,6 +3923,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "key" @@ -3043,6 +3951,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "flags" @@ -3083,6 +3999,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -3103,6 +4023,10 @@ "CALL_FUNCTION_KW", "nested_dict.get_or_create_nest(key_parent, access_lists=False)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -3151,6 +4075,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Duplicate inline table key {key_stem!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")" @@ -3191,6 +4119,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3211,6 +4143,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -3303,6 +4239,14 @@ "CALL_FUNCTION_KW", "flags.set(key, Flags.FROZEN, recursive=True)" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3323,6 +4267,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3343,6 +4291,18 @@ "BINARY_SUBSCR", "src[pos : pos + 2]" ], + [ + "STORE_FAST", + "escape_id" + ], + [ + "LOAD_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "pos += 2" + ], [ "LOAD_FAST", "multiline" @@ -3383,6 +4343,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3395,6 +4359,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -3427,6 +4395,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3447,6 +4423,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3575,6 +4555,10 @@ "BINARY_SUBSCR", "src[pos : pos + hex_len]" ], + [ + "STORE_FAST", + "hex_str" + ], [ "LOAD_GLOBAL", "len" @@ -3627,10 +4611,18 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid hex value\")" ], + [ + "LOAD_FAST", + "pos += hex_len" + ], [ "LOAD_FAST", "hex_len" ], + [ + "STORE_FAST", + "pos += hex_len" + ], [ "LOAD_GLOBAL", "int" @@ -3643,6 +4635,10 @@ "CALL_FUNCTION", "int(hex_str, 16)" ], + [ + "STORE_FAST", + "hex_int" + ], [ "LOAD_GLOBAL", "is_unicode_scalar_value" @@ -3687,10 +4683,22 @@ "CALL_FUNCTION", "chr(hex_int)" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3711,6 +4719,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3735,6 +4747,14 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos += 3" + ], + [ + "STORE_FAST", + "pos += 3" + ], [ "LOAD_FAST", "src" @@ -3751,10 +4771,22 @@ "CALL_METHOD", "src.startswith(\"\\n\", pos)" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "literal" ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3775,6 +4807,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )" ], + [ + "STORE_FAST", + "end_pos" + ], [ "LOAD_FAST", "src" @@ -3791,6 +4827,10 @@ "BINARY_SUBSCR", "src[pos:end_pos]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "end_pos" @@ -3799,6 +4839,14 @@ "BINARY_ADD", "end_pos + 3" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -3815,6 +4863,14 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=True)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "src" @@ -3843,6 +4899,14 @@ "LOAD_FAST", "result" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "src" @@ -3879,6 +4943,14 @@ "BINARY_ADD", "result + delim" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "pos" @@ -3907,22 +4979,46 @@ "LOAD_GLOBAL", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape_multiline" ], + [ + "STORE_FAST", + "parse_escapes" + ], [ "LOAD_GLOBAL", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape" ], + [ + "STORE_FAST", + "parse_escapes" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "src" @@ -3935,6 +5031,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -4047,6 +5147,14 @@ "BINARY_ADD", "result + src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "char" @@ -4055,6 +5163,10 @@ "COMPARE_OP", "char == \"\\\\\"" ], + [ + "LOAD_FAST", + "result += src[start_pos:pos]" + ], [ "LOAD_FAST", "src" @@ -4071,6 +5183,10 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "STORE_FAST", + "result += src[start_pos:pos]" + ], [ "LOAD_FAST", "parse_escapes" @@ -4087,14 +5203,34 @@ "CALL_FUNCTION", "parse_escapes(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "parsed_escape" + ], + [ + "LOAD_FAST", + "result += parsed_escape" + ], [ "LOAD_FAST", "parsed_escape" ], + [ + "STORE_FAST", + "result += parsed_escape" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "char" @@ -4123,10 +5259,22 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Illegal character {char!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Illegal character {char!r}\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "src" @@ -4139,10 +5287,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -4395,6 +5551,10 @@ "CALL_METHOD", "RE_DATETIME.match(src, pos)" ], + [ + "STORE_FAST", + "datetime_match" + ], [ "LOAD_FAST", "datetime_match" @@ -4411,6 +5571,10 @@ "CALL_FUNCTION", "match_to_datetime(datetime_match)" ], + [ + "STORE_FAST", + "datetime_obj" + ], [ "LOAD_GLOBAL", "ValueError" @@ -4471,6 +5635,10 @@ "CALL_METHOD", "RE_LOCALTIME.match(src, pos)" ], + [ + "STORE_FAST", + "localtime_match" + ], [ "LOAD_FAST", "localtime_match" @@ -4519,6 +5687,10 @@ "CALL_METHOD", "RE_NUMBER.match(src, pos)" ], + [ + "STORE_FAST", + "number_match" + ], [ "LOAD_FAST", "number_match" @@ -4571,6 +5743,10 @@ "BINARY_SUBSCR", "src[pos : pos + 3]" ], + [ + "STORE_FAST", + "first_three" + ], [ "LOAD_FAST", "first_three" @@ -4619,6 +5795,10 @@ "BINARY_SUBSCR", "src[pos : pos + 4]" ], + [ + "STORE_FAST", + "first_four" + ], [ "LOAD_FAST", "first_four" @@ -4663,6 +5843,10 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid value\")" ], + [ + "STORE_FAST", + " def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"" + ], [ "LOAD_GLOBAL", "TOMLDecodeError" @@ -4687,6 +5871,10 @@ "CALL_FUNCTION", "" ], + [ + "BUILD_STRING", + "f\"{msg} (at {coord_repr(src, pos)})\"" + ], [ "CALL_FUNCTION", "TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" @@ -4731,6 +5919,10 @@ "BINARY_ADD", "src.count(\"\\n\", 0, pos) + 1" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "line" @@ -4747,6 +5939,10 @@ "BINARY_ADD", "pos + 1" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "pos" @@ -4771,6 +5967,10 @@ "BINARY_SUBTRACT", "pos - src.rindex(\"\\n\", 0, pos)" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "" @@ -4779,14 +5979,34 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"line {line}, column {column}\"" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], [ "LOAD_DEREF", "parse_float" @@ -4803,6 +6023,10 @@ "LOAD_GLOBAL", "float" ], + [ + "STORE_FAST", + " def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value" + ], [ "LOAD_FAST", "safe_parse_float" @@ -4819,6 +6043,10 @@ "CALL_FUNCTION", "parse_float(float_str)" ], + [ + "STORE_FAST", + "float_value" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/_parser-py-3.9.json b/tests/sample_results/_parser-py-3.9.json index 063476b..485268e 100644 --- a/tests/sample_results/_parser-py-3.9.json +++ b/tests/sample_results/_parser-py-3.9.json @@ -1,4 +1,68 @@ [ + [ + "STORE_NAME", + "from __future__ import annotations" + ], + [ + "STORE_NAME", + "from collections.abc import Iterable" + ], + [ + "STORE_NAME", + "import string" + ], + [ + "STORE_NAME", + "from types import MappingProxyType" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from typing import Any, BinaryIO, NamedTuple" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._re import (\n RE_DATETIME,\n RE_LOCALTIME,\n RE_NUMBER,\n match_to_datetime,\n match_to_localtime,\n match_to_number,\n)" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], + [ + "STORE_NAME", + "from ._types import Key, ParseFloat, Pos" + ], [ "LOAD_NAME", "frozenset" @@ -11,6 +75,10 @@ "CALL_FUNCTION", "range(32)" ], + [ + "CALL_FUNCTION", + "(chr(i) for i in range(32))" + ], [ "CALL_FUNCTION", "frozenset(chr(i) for i in range(32))" @@ -35,6 +103,10 @@ "BINARY_OR", "frozenset(chr(i) for i in range(32)) | frozenset(chr(127))" ], + [ + "STORE_NAME", + "ASCII_CTRL" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -51,6 +123,10 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\")" ], + [ + "STORE_NAME", + "ILLEGAL_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ASCII_CTRL" @@ -67,18 +143,34 @@ "BINARY_SUBTRACT", "ASCII_CTRL - frozenset(\"\\t\\n\")" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_BASIC_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_MULTILINE_LITERAL_STR_CHARS" + ], [ "LOAD_NAME", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_NAME", + "ILLEGAL_COMMENT_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -87,6 +179,10 @@ "CALL_FUNCTION", "frozenset(\" \\t\")" ], + [ + "STORE_NAME", + "TOML_WS" + ], [ "LOAD_NAME", "TOML_WS" @@ -103,6 +199,10 @@ "BINARY_OR", "TOML_WS | frozenset(\"\\n\")" ], + [ + "STORE_NAME", + "TOML_WS_AND_NEWLINE" + ], [ "LOAD_NAME", "frozenset" @@ -135,6 +235,10 @@ "CALL_FUNCTION", "frozenset(string.ascii_letters + string.digits + \"-_\")" ], + [ + "STORE_NAME", + "BARE_KEY_CHARS" + ], [ "LOAD_NAME", "BARE_KEY_CHARS" @@ -151,6 +255,10 @@ "BINARY_OR", "BARE_KEY_CHARS | frozenset(\"\\\"'\")" ], + [ + "STORE_NAME", + "KEY_INITIAL_CHARS" + ], [ "LOAD_NAME", "frozenset" @@ -167,6 +275,10 @@ "CALL_FUNCTION", "frozenset(string.hexdigits)" ], + [ + "STORE_NAME", + "HEXDIGIT_CHARS" + ], [ "LOAD_NAME", "MappingProxyType" @@ -175,22 +287,166 @@ "CALL_FUNCTION", "MappingProxyType(\n {\n \"\\\\b\": \"\\u0008\", # backspace\n \"\\\\t\": \"\\u0009\", # tab\n \"\\\\n\": \"\\u000A\", # linefeed\n \"\\\\f\": \"\\u000C\", # form feed\n \"\\\\r\": \"\\u000D\", # carriage return\n '\\\\\"': \"\\u0022\", # quote\n \"\\\\\\\\\": \"\\u005C\", # backslash\n }\n)" ], + [ + "STORE_NAME", + "BASIC_STR_ESCAPE_REPLACEMENTS" + ], [ "LOAD_NAME", "ValueError" ], + [ + "CALL_FUNCTION", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def load(fp: BinaryIO, /, *, parse_float: ParseFloat = float) -> dict[str, Any]:\n \"\"\"Parse TOML from a binary file object.\"\"\"\n b = fp.read()\n try:\n s = b.decode()\n except AttributeError:\n raise TypeError(\n \"File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`\"\n ) from None\n return loads(s, parse_float=parse_float)" + ], [ "LOAD_NAME", "float" ], + [ + "STORE_NAME", + "def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa: C901\n \"\"\"Parse TOML from a string.\"\"\"\n\n # The spec allows converting \"\\r\\n\" to \"\\n\", even in string\n # literals. Let's do so to simplify parsing.\n src = s.replace(\"\\r\\n\", \"\\n\")\n pos = 0\n out = Output(NestedDict(), Flags())\n header: Key = ()\n parse_float = make_safe_parse_float(parse_float)\n\n # Parse one statement at a time\n # (typically means one line in TOML source)\n while True:\n # 1. Skip line leading whitespace\n pos = skip_chars(src, pos, TOML_WS)\n\n # 2. Parse rules. Expect one of the following:\n # - end of file\n # - end of line\n # - comment\n # - key/value pair\n # - append dict to list (and move to its namespace)\n # - create dict (and move to its namespace)\n # Skip trailing whitespace when applicable.\n try:\n char = src[pos]\n except IndexError:\n break\n if char == \"\\n\":\n pos += 1\n continue\n if char in KEY_INITIAL_CHARS:\n pos = key_value_rule(src, pos, out, header, parse_float)\n pos = skip_chars(src, pos, TOML_WS)\n elif char == \"[\":\n try:\n second_char: str | None = src[pos + 1]\n except IndexError:\n second_char = None\n out.flags.finalize_pending()\n if second_char == \"[\":\n pos, header = create_list_rule(src, pos, out)\n else:\n pos, header = create_dict_rule(src, pos, out)\n pos = skip_chars(src, pos, TOML_WS)\n elif char != \"#\":\n raise suffixed_err(src, pos, \"Invalid statement\")\n\n # 3. Skip comment\n pos = skip_comment(src, pos)\n\n # 4. Expect end of line or end of file\n try:\n char = src[pos]\n except IndexError:\n break\n if char != \"\\n\":\n raise suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )\n pos += 1\n\n return out.data.dict" + ], + [ + "CALL_FUNCTION", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "CALL_FUNCTION", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], [ "LOAD_NAME", "NamedTuple" ], + [ + "CALL_FUNCTION", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:\n try:\n while src[pos] in chars:\n pos += 1\n except IndexError:\n pass\n return pos" + ], + [ + "STORE_NAME", + "def skip_until(\n src: str,\n pos: Pos,\n expect: str,\n *,\n error_on: frozenset[str],\n error_on_eof: bool,\n) -> Pos:\n try:\n new_pos = src.index(expect, pos)\n except ValueError:\n new_pos = len(src)\n if error_on_eof:\n raise suffixed_err(src, new_pos, f\"Expected {expect!r}\") from None\n\n if not error_on.isdisjoint(src[pos:new_pos]):\n while src[pos] not in error_on:\n pos += 1\n raise suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")\n return new_pos" + ], + [ + "STORE_NAME", + "def skip_comment(src: str, pos: Pos) -> Pos:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char == \"#\":\n return skip_until(\n src, pos + 1, \"\\n\", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False\n )\n return pos" + ], + [ + "STORE_NAME", + "def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:\n while True:\n pos_before_skip = pos\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n pos = skip_comment(src, pos)\n if pos == pos_before_skip:\n return pos" + ], + [ + "STORE_NAME", + "def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 1 # Skip \"[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot declare {key} twice\")\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.get_or_create_nest(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]\", pos):\n raise suffixed_err(src, pos, \"Expected ']' at the end of a table declaration\")\n return pos + 1, key" + ], + [ + "STORE_NAME", + "def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:\n pos += 2 # Skip \"[[\"\n pos = skip_chars(src, pos, TOML_WS)\n pos, key = parse_key(src, pos)\n\n if out.flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n # Free the namespace now that it points to another empty list item...\n out.flags.unset_all(key)\n # ...but this key precisely is still prohibited from table declaration\n out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)\n try:\n out.data.append_nest_to_list(key)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n\n if not src.startswith(\"]]\", pos):\n raise suffixed_err(src, pos, \"Expected ']]' at the end of an array declaration\")\n return pos + 2, key" + ], + [ + "STORE_NAME", + "def key_value_rule(\n src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat\n) -> Pos:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n abs_key_parent = header + key_parent\n\n relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))\n for cont_key in relative_path_cont_keys:\n # Check that dotted key syntax does not redefine an existing table\n if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):\n raise suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")\n # Containers in the relative path can't be opened with the table syntax or\n # dotted key/value syntax in following table sections.\n out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)\n\n if out.flags.is_(abs_key_parent, Flags.FROZEN):\n raise suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )\n\n try:\n nest = out.data.get_or_create_nest(abs_key_parent)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\")\n # Mark inline table and array namespaces recursively immutable\n if isinstance(value, (dict, list)):\n out.flags.set(header + key, Flags.FROZEN, recursive=True)\n nest[key_stem] = value\n return pos" + ], + [ + "STORE_NAME", + "def parse_key_value_pair(\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Key, Any]:\n pos, key = parse_key(src, pos)\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \"=\":\n raise suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, value = parse_value(src, pos, parse_float)\n return pos, key, value" + ], + [ + "STORE_NAME", + "def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:\n pos, key_part = parse_key_part(src, pos)\n key: Key = (key_part,)\n pos = skip_chars(src, pos, TOML_WS)\n while True:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char != \".\":\n return pos, key\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)\n pos, key_part = parse_key_part(src, pos)\n key += (key_part,)\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n if char in BARE_KEY_CHARS:\n start_pos = pos\n pos = skip_chars(src, pos, BARE_KEY_CHARS)\n return pos, src[start_pos:pos]\n if char == \"'\":\n return parse_literal_str(src, pos)\n if char == '\"':\n return parse_one_line_basic_str(src, pos)\n raise suffixed_err(src, pos, \"Invalid initial character for a key part\")" + ], + [ + "STORE_NAME", + "def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1\n return parse_basic_str(src, pos, multiline=False)" + ], + [ + "STORE_NAME", + "def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]:\n pos += 1\n array: list = []\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array\n while True:\n pos, val = parse_value(src, pos, parse_float)\n array.append(val)\n pos = skip_comments_and_array_ws(src, pos)\n\n c = src[pos : pos + 1]\n if c == \"]\":\n return pos + 1, array\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed array\")\n pos += 1\n\n pos = skip_comments_and_array_ws(src, pos)\n if src.startswith(\"]\", pos):\n return pos + 1, array" + ], + [ + "STORE_NAME", + "def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]:\n pos += 1\n nested_dict = NestedDict()\n flags = Flags()\n\n pos = skip_chars(src, pos, TOML_WS)\n if src.startswith(\"}\", pos):\n return pos + 1, nested_dict.dict\n while True:\n pos, key, value = parse_key_value_pair(src, pos, parse_float)\n key_parent, key_stem = key[:-1], key[-1]\n if flags.is_(key, Flags.FROZEN):\n raise suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")\n try:\n nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)\n except KeyError:\n raise suffixed_err(src, pos, \"Cannot overwrite a value\") from None\n if key_stem in nest:\n raise suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")\n nest[key_stem] = value\n pos = skip_chars(src, pos, TOML_WS)\n c = src[pos : pos + 1]\n if c == \"}\":\n return pos + 1, nested_dict.dict\n if c != \",\":\n raise suffixed_err(src, pos, \"Unclosed inline table\")\n if isinstance(value, (dict, list)):\n flags.set(key, Flags.FROZEN, recursive=True)\n pos += 1\n pos = skip_chars(src, pos, TOML_WS)" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape(\n src: str, pos: Pos, *, multiline: bool = False\n) -> tuple[Pos, str]:\n escape_id = src[pos : pos + 2]\n pos += 2\n if multiline and escape_id in {\"\\\\ \", \"\\\\\\t\", \"\\\\\\n\"}:\n # Skip whitespace until next non-whitespace character or end of\n # the doc. Error if non-whitespace is found before newline.\n if escape_id != \"\\\\\\n\":\n pos = skip_chars(src, pos, TOML_WS)\n try:\n char = src[pos]\n except IndexError:\n return pos, \"\"\n if char != \"\\n\":\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")\n pos += 1\n pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)\n return pos, \"\"\n if escape_id == \"\\\\u\":\n return parse_hex_char(src, pos, 4)\n if escape_id == \"\\\\U\":\n return parse_hex_char(src, pos, 8)\n try:\n return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]\n except KeyError:\n raise suffixed_err(src, pos, \"Unescaped '\\\\' in a string\") from None" + ], + [ + "STORE_NAME", + "def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:\n return parse_basic_str_escape(src, pos, multiline=True)" + ], + [ + "STORE_NAME", + "def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:\n hex_str = src[pos : pos + hex_len]\n if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):\n raise suffixed_err(src, pos, \"Invalid hex value\")\n pos += hex_len\n hex_int = int(hex_str, 16)\n if not is_unicode_scalar_value(hex_int):\n raise suffixed_err(src, pos, \"Escaped character is not a Unicode scalar value\")\n return pos, chr(hex_int)" + ], + [ + "STORE_NAME", + "def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:\n pos += 1 # Skip starting apostrophe\n start_pos = pos\n pos = skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )\n return pos + 1, src[start_pos:pos]" + ], + [ + "STORE_NAME", + "def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:\n pos += 3\n if src.startswith(\"\\n\", pos):\n pos += 1\n\n if literal:\n delim = \"'\"\n end_pos = skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )\n result = src[pos:end_pos]\n pos = end_pos + 3\n else:\n delim = '\"'\n pos, result = parse_basic_str(src, pos, multiline=True)\n\n # Add at maximum two extra apostrophes/quotes if the end sequence\n # is 4 or 5 chars long instead of just 3.\n if not src.startswith(delim, pos):\n return pos, result\n pos += 1\n if not src.startswith(delim, pos):\n return pos, result + delim\n pos += 1\n return pos, result + (delim * 2)" + ], + [ + "STORE_NAME", + "def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:\n if multiline:\n error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape_multiline\n else:\n error_on = ILLEGAL_BASIC_STR_CHARS\n parse_escapes = parse_basic_str_escape\n result = \"\"\n start_pos = pos\n while True:\n try:\n char = src[pos]\n except IndexError:\n raise suffixed_err(src, pos, \"Unterminated string\") from None\n if char == '\"':\n if not multiline:\n return pos + 1, result + src[start_pos:pos]\n if src.startswith('\"\"\"', pos):\n return pos + 3, result + src[start_pos:pos]\n pos += 1\n continue\n if char == \"\\\\\":\n result += src[start_pos:pos]\n pos, parsed_escape = parse_escapes(src, pos)\n result += parsed_escape\n start_pos = pos\n continue\n if char in error_on:\n raise suffixed_err(src, pos, f\"Illegal character {char!r}\")\n pos += 1" + ], + [ + "STORE_NAME", + "def parse_value( # noqa: C901\n src: str, pos: Pos, parse_float: ParseFloat\n) -> tuple[Pos, Any]:\n try:\n char: str | None = src[pos]\n except IndexError:\n char = None\n\n # IMPORTANT: order conditions based on speed of checking and likelihood\n\n # Basic strings\n if char == '\"':\n if src.startswith('\"\"\"', pos):\n return parse_multiline_str(src, pos, literal=False)\n return parse_one_line_basic_str(src, pos)\n\n # Literal strings\n if char == \"'\":\n if src.startswith(\"'''\", pos):\n return parse_multiline_str(src, pos, literal=True)\n return parse_literal_str(src, pos)\n\n # Booleans\n if char == \"t\":\n if src.startswith(\"true\", pos):\n return pos + 4, True\n if char == \"f\":\n if src.startswith(\"false\", pos):\n return pos + 5, False\n\n # Arrays\n if char == \"[\":\n return parse_array(src, pos, parse_float)\n\n # Inline tables\n if char == \"{\":\n return parse_inline_table(src, pos, parse_float)\n\n # Dates and times\n datetime_match = RE_DATETIME.match(src, pos)\n if datetime_match:\n try:\n datetime_obj = match_to_datetime(datetime_match)\n except ValueError as e:\n raise suffixed_err(src, pos, \"Invalid date or datetime\") from e\n return datetime_match.end(), datetime_obj\n localtime_match = RE_LOCALTIME.match(src, pos)\n if localtime_match:\n return localtime_match.end(), match_to_localtime(localtime_match)\n\n # Integers and \"normal\" floats.\n # The regex will greedily match any type starting with a decimal\n # char, so needs to be located after handling of dates and times.\n number_match = RE_NUMBER.match(src, pos)\n if number_match:\n return number_match.end(), match_to_number(number_match, parse_float)\n\n # Special floats\n first_three = src[pos : pos + 3]\n if first_three in {\"inf\", \"nan\"}:\n return pos + 3, parse_float(first_three)\n first_four = src[pos : pos + 4]\n if first_four in {\"-inf\", \"+inf\", \"-nan\", \"+nan\"}:\n return pos + 4, parse_float(first_four)\n\n raise suffixed_err(src, pos, \"Invalid value\")" + ], + [ + "STORE_NAME", + "def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError:\n \"\"\"Return a `TOMLDecodeError` where error message is suffixed with\n coordinates in source.\"\"\"\n\n def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"\n\n return TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" + ], + [ + "STORE_NAME", + "def is_unicode_scalar_value(codepoint: int) -> bool:\n return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)" + ], + [ + "STORE_NAME", + "def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:\n \"\"\"A decorator to make `parse_float` safe.\n\n `parse_float` must not return dicts or lists, because these types\n would be mixed with parsed TOML tables and arrays, thus confusing\n the parser. The returned decorated callable raises `ValueError`\n instead of returning illegal types.\n \"\"\"\n # The default `float` callable never returns illegal types. Optimize it.\n if parse_float is float: # type: ignore[comparison-overlap]\n return float\n\n def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value\n\n return safe_parse_float" + ], + [ + "LOAD_FAST", + "(chr(i) for i in range(32))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "chr" @@ -203,6 +459,22 @@ "CALL_FUNCTION", "chr(i)" ], + [ + "LOAD_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "class TOMLDecodeError(ValueError):\n \"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"An error raised if a document is not valid TOML.\"\"\"" + ], [ "LOAD_FAST", "fp" @@ -215,6 +487,10 @@ "CALL_METHOD", "fp.read()" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "b" @@ -227,6 +503,10 @@ "CALL_METHOD", "b.decode()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -267,6 +547,14 @@ "CALL_METHOD", "s.replace(\"\\r\\n\", \"\\n\")" ], + [ + "STORE_FAST", + "src" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "Output" @@ -291,6 +579,14 @@ "CALL_FUNCTION", "Output(NestedDict(), Flags())" ], + [ + "STORE_FAST", + "out" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "make_safe_parse_float" @@ -303,6 +599,10 @@ "CALL_FUNCTION", "make_safe_parse_float(parse_float)" ], + [ + "STORE_FAST", + "parse_float" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -323,6 +623,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -335,6 +639,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -347,6 +655,14 @@ "COMPARE_OP", "char == \"\\n\"" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -387,6 +703,10 @@ "CALL_FUNCTION", "key_value_rule(src, pos, out, header, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -407,6 +727,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -431,10 +755,18 @@ "BINARY_SUBSCR", "src[pos + 1]" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "second_char" + ], [ "LOAD_FAST", "out" @@ -479,6 +811,14 @@ "CALL_FUNCTION", "create_list_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "create_dict_rule" @@ -499,6 +839,14 @@ "CALL_FUNCTION", "create_dict_rule(src, pos, out)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "header" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -519,6 +867,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -559,6 +911,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -571,6 +927,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -599,6 +959,14 @@ "CALL_FUNCTION", "suffixed_err(\n src, pos, \"Expected newline or end of document after a statement\"\n )" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "out" @@ -611,6 +979,54 @@ "LOAD_ATTR", "out.data.dict" ], + [ + "LOAD_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "class Flags:\n \"\"\"Flags that map to parsed keys/namespaces.\"\"\"\n\n # Marks an immutable namespace (inline array or inline table).\n FROZEN = 0\n # Marks a nest that has been explicitly created and can no longer\n # be opened using the \"[table]\" syntax.\n EXPLICIT_NEST = 1\n\n def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()\n\n def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))\n\n def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()\n\n def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)\n\n def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)\n\n def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], + [ + "STORE_NAME", + "\"\"\"Flags that map to parsed keys/namespaces.\"\"\"" + ], + [ + "STORE_NAME", + "FROZEN" + ], + [ + "STORE_NAME", + "EXPLICIT_NEST" + ], + [ + "STORE_NAME", + " def __init__(self) -> None:\n self._flags: dict[str, dict] = {}\n self._pending_flags: set[tuple[Key, int]] = set()" + ], + [ + "STORE_NAME", + " def add_pending(self, key: Key, flag: int) -> None:\n self._pending_flags.add((key, flag))" + ], + [ + "STORE_NAME", + " def finalize_pending(self) -> None:\n for key, flag in self._pending_flags:\n self.set(key, flag, recursive=False)\n self._pending_flags.clear()" + ], + [ + "STORE_NAME", + " def unset_all(self, key: Key) -> None:\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return\n cont = cont[k][\"nested\"]\n cont.pop(key[-1], None)" + ], + [ + "STORE_NAME", + " def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003\n cont = self._flags\n key_parent, key_stem = key[:-1], key[-1]\n for k in key_parent:\n if k not in cont:\n cont[k] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont = cont[k][\"nested\"]\n if key_stem not in cont:\n cont[key_stem] = {\"flags\": set(), \"recursive_flags\": set(), \"nested\": {}}\n cont[key_stem][\"recursive_flags\" if recursive else \"flags\"].add(flag)" + ], + [ + "STORE_NAME", + " def is_(self, key: Key, flag: int) -> bool:\n if not key:\n return False # document root has no flags\n cont = self._flags\n for k in key[:-1]:\n if k not in cont:\n return False\n inner_cont = cont[k]\n if flag in inner_cont[\"recursive_flags\"]:\n return True\n cont = inner_cont[\"nested\"]\n key_stem = key[-1]\n if key_stem in cont:\n cont = cont[key_stem]\n return flag in cont[\"flags\"] or flag in cont[\"recursive_flags\"]\n return False" + ], [ "LOAD_FAST", "self" @@ -667,6 +1083,14 @@ "LOAD_ATTR", "self._pending_flags" ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "flag" + ], [ "LOAD_FAST", "self" @@ -711,6 +1135,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -719,6 +1147,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -747,6 +1179,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "cont" @@ -775,6 +1211,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -791,10 +1231,22 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_parent" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -851,6 +1303,10 @@ "BINARY_SUBSCR", "cont[k][\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key_stem" @@ -935,6 +1391,10 @@ "LOAD_ATTR", "self._flags" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -943,6 +1403,10 @@ "BINARY_SUBSCR", "key[:-1]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -967,6 +1431,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "inner_cont" + ], [ "LOAD_FAST", "flag" @@ -991,6 +1459,10 @@ "BINARY_SUBSCR", "inner_cont[\"nested\"]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -999,6 +1471,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "key_stem" @@ -1023,6 +1499,10 @@ "BINARY_SUBSCR", "cont[key_stem]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "flag" @@ -1055,6 +1535,30 @@ "CONTAINS_OP", "flag in cont[\"recursive_flags\"]" ], + [ + "LOAD_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + "class NestedDict:\n def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}\n\n def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont\n\n def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], + [ + "STORE_NAME", + " def __init__(self) -> None:\n # The parsed content of the TOML document\n self.dict: dict[str, Any] = {}" + ], + [ + "STORE_NAME", + " def get_or_create_nest(\n self,\n key: Key,\n *,\n access_lists: bool = True,\n ) -> dict:\n cont: Any = self.dict\n for k in key:\n if k not in cont:\n cont[k] = {}\n cont = cont[k]\n if access_lists and isinstance(cont, list):\n cont = cont[-1]\n if not isinstance(cont, dict):\n raise KeyError(\"There is no nest behind this key\")\n return cont" + ], + [ + "STORE_NAME", + " def append_nest_to_list(self, key: Key) -> None:\n cont = self.get_or_create_nest(key[:-1])\n last_key = key[-1]\n if last_key in cont:\n list_ = cont[last_key]\n if not isinstance(list_, list):\n raise KeyError(\"An object other than list found behind this key\")\n list_.append({})\n else:\n cont[last_key] = [{}]" + ], [ "LOAD_FAST", "self" @@ -1071,10 +1575,18 @@ "LOAD_ATTR", "self.dict" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "k" @@ -1111,6 +1623,10 @@ "BINARY_SUBSCR", "cont[k]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "access_lists" @@ -1139,6 +1655,10 @@ "BINARY_SUBSCR", "cont[-1]" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1187,6 +1707,10 @@ "CALL_METHOD", "self.get_or_create_nest(key[:-1])" ], + [ + "STORE_FAST", + "cont" + ], [ "LOAD_FAST", "key" @@ -1195,6 +1719,10 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "last_key" + ], [ "LOAD_FAST", "last_key" @@ -1219,6 +1747,10 @@ "BINARY_SUBSCR", "cont[last_key]" ], + [ + "STORE_FAST", + "list_" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1267,6 +1799,34 @@ "STORE_SUBSCR", "cont[last_key]" ], + [ + "LOAD_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "STORE_NAME", + "class Output(NamedTuple):\n data: NestedDict\n flags: Flags" + ], + [ + "LOAD_NAME", + "data: NestedDict" + ], + [ + "STORE_SUBSCR", + "data: NestedDict" + ], + [ + "LOAD_NAME", + "flags: Flags" + ], + [ + "STORE_SUBSCR", + "flags: Flags" + ], [ "LOAD_FAST", "src" @@ -1287,6 +1847,14 @@ "CONTAINS_OP", "src[pos] in chars" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "IndexError" @@ -1315,6 +1883,10 @@ "CALL_METHOD", "src.index(expect, pos)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_GLOBAL", "ValueError" @@ -1331,6 +1903,10 @@ "CALL_FUNCTION", "len(src)" ], + [ + "STORE_FAST", + "new_pos" + ], [ "LOAD_FAST", "error_on_eof" @@ -1351,6 +1927,10 @@ "LOAD_FAST", "expect" ], + [ + "BUILD_STRING", + "f\"Expected {expect!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, new_pos, f\"Expected {expect!r}\")" @@ -1403,6 +1983,14 @@ "CONTAINS_OP", "src[pos] not in error_on" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "suffixed_err" @@ -1427,6 +2015,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "BUILD_STRING", + "f\"Found invalid character {src[pos]!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Found invalid character {src[pos]!r}\")" @@ -1447,10 +2039,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -1491,6 +2091,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos_before_skip" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1511,6 +2115,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_comment" @@ -1527,6 +2135,10 @@ "CALL_FUNCTION", "skip_comment(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -1543,6 +2155,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1563,6 +2183,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1579,6 +2203,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1651,6 +2283,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot declare {key} twice\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot declare {key} twice\")" @@ -1767,6 +2403,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -1787,6 +2431,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key" @@ -1803,6 +2451,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "out" @@ -1847,6 +2503,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -2003,6 +2663,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_DEREF", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_DEREF", "key" @@ -2019,6 +2691,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_DEREF", "header" @@ -2031,6 +2711,10 @@ "BINARY_ADD", "header + key_parent" ], + [ + "STORE_FAST", + "abs_key_parent" + ], [ "LOAD_GLOBAL", "range" @@ -2051,10 +2735,22 @@ "CALL_FUNCTION", "range(1, len(key))" ], + [ + "CALL_FUNCTION", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "relative_path_cont_keys" + ], [ "LOAD_FAST", "relative_path_cont_keys" ], + [ + "STORE_FAST", + "cont_key" + ], [ "LOAD_FAST", "out" @@ -2099,6 +2795,10 @@ "LOAD_FAST", "cont_key" ], + [ + "BUILD_STRING", + "f\"Cannot redefine namespace {cont_key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot redefine namespace {cont_key}\")" @@ -2175,6 +2875,10 @@ "LOAD_FAST", "abs_key_parent" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {abs_key_parent}\"" + ], [ "CALL_FUNCTION", "suffixed_err(\n src, pos, f\"Cannot mutate immutable namespace {abs_key_parent}\"\n )" @@ -2199,6 +2903,10 @@ "CALL_METHOD", "out.data.get_or_create_nest(abs_key_parent)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -2323,6 +3031,14 @@ "LOAD_FAST", "pos" ], + [ + "LOAD_FAST", + "(header + key[:i] for i in range(1, len(key)))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_DEREF", "header" @@ -2359,6 +3075,14 @@ "CALL_FUNCTION", "parse_key(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "src" @@ -2371,10 +3095,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2399,6 +3131,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Expected '=' after a key in a key/value pair\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2419,6 +3159,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_value" @@ -2439,6 +3183,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "pos" @@ -2467,10 +3219,22 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2491,6 +3255,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2503,10 +3271,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2523,6 +3299,14 @@ "LOAD_FAST", "key" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2543,6 +3327,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_key_part" @@ -2559,10 +3347,26 @@ "CALL_FUNCTION", "parse_key_part(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key_part" + ], + [ + "LOAD_FAST", + "key" + ], [ "LOAD_FAST", "key_part" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2583,6 +3387,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2595,10 +3403,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -2615,6 +3431,10 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2635,6 +3455,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, BARE_KEY_CHARS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -2719,6 +3543,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid initial character for a key part\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -2735,6 +3567,18 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=False)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "array" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2751,6 +3595,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2799,6 +3647,14 @@ "CALL_FUNCTION", "parse_value(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "val" + ], [ "LOAD_FAST", "array" @@ -2831,6 +3687,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2851,6 +3711,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -2895,6 +3759,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unclosed array\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_comments_and_array_ws" @@ -2911,6 +3783,10 @@ "CALL_FUNCTION", "skip_comments_and_array_ws(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -2939,6 +3815,14 @@ "LOAD_FAST", "array" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "NestedDict" @@ -2947,6 +3831,10 @@ "CALL_FUNCTION", "NestedDict()" ], + [ + "STORE_FAST", + "nested_dict" + ], [ "LOAD_GLOBAL", "Flags" @@ -2955,6 +3843,10 @@ "CALL_FUNCTION", "Flags()" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -2975,6 +3867,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3027,6 +3923,18 @@ "CALL_FUNCTION", "parse_key_value_pair(src, pos, parse_float)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "key" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "key" @@ -3043,6 +3951,14 @@ "BINARY_SUBSCR", "key[-1]" ], + [ + "STORE_FAST", + "key_parent" + ], + [ + "STORE_FAST", + "key_stem" + ], [ "LOAD_FAST", "flags" @@ -3083,6 +3999,10 @@ "LOAD_FAST", "key" ], + [ + "BUILD_STRING", + "f\"Cannot mutate immutable namespace {key}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Cannot mutate immutable namespace {key}\")" @@ -3103,6 +4023,10 @@ "CALL_FUNCTION_KW", "nested_dict.get_or_create_nest(key_parent, access_lists=False)" ], + [ + "STORE_FAST", + "nest" + ], [ "LOAD_GLOBAL", "KeyError" @@ -3151,6 +4075,10 @@ "LOAD_FAST", "key_stem" ], + [ + "BUILD_STRING", + "f\"Duplicate inline table key {key_stem!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Duplicate inline table key {key_stem!r}\")" @@ -3191,6 +4119,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3211,6 +4143,10 @@ "BINARY_SUBSCR", "src[pos : pos + 1]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_FAST", "c" @@ -3303,6 +4239,14 @@ "CALL_FUNCTION_KW", "flags.set(key, Flags.FROZEN, recursive=True)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3323,6 +4267,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3343,6 +4291,18 @@ "BINARY_SUBSCR", "src[pos : pos + 2]" ], + [ + "STORE_FAST", + "escape_id" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "multiline" @@ -3383,6 +4343,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3395,6 +4359,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -3427,6 +4395,14 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Unescaped '\\\\' in a string\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "skip_chars" @@ -3447,6 +4423,10 @@ "CALL_FUNCTION", "skip_chars(src, pos, TOML_WS_AND_NEWLINE)" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3575,6 +4555,10 @@ "BINARY_SUBSCR", "src[pos : pos + hex_len]" ], + [ + "STORE_FAST", + "hex_str" + ], [ "LOAD_GLOBAL", "len" @@ -3627,10 +4611,18 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid hex value\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "hex_len" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -3643,6 +4635,10 @@ "CALL_FUNCTION", "int(hex_str, 16)" ], + [ + "STORE_FAST", + "hex_int" + ], [ "LOAD_GLOBAL", "is_unicode_scalar_value" @@ -3691,6 +4687,18 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3711,6 +4719,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src, pos, \"'\", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True\n )" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -3735,6 +4747,14 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3751,10 +4771,22 @@ "CALL_METHOD", "src.startswith(\"\\n\", pos)" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "literal" ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "skip_until" @@ -3775,6 +4807,10 @@ "CALL_FUNCTION_KW", "skip_until(\n src,\n pos,\n \"'''\",\n error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,\n error_on_eof=True,\n )" ], + [ + "STORE_FAST", + "end_pos" + ], [ "LOAD_FAST", "src" @@ -3791,6 +4827,10 @@ "BINARY_SUBSCR", "src[pos:end_pos]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "end_pos" @@ -3799,6 +4839,14 @@ "BINARY_ADD", "end_pos + 3" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "delim" + ], [ "LOAD_GLOBAL", "parse_basic_str" @@ -3815,6 +4863,14 @@ "CALL_FUNCTION_KW", "parse_basic_str(src, pos, multiline=True)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "src" @@ -3843,6 +4899,14 @@ "LOAD_FAST", "result" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -3883,6 +4947,14 @@ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "pos" + ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "result" @@ -3907,22 +4979,46 @@ "LOAD_GLOBAL", "ILLEGAL_MULTILINE_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape_multiline" ], + [ + "STORE_FAST", + "parse_escapes" + ], [ "LOAD_GLOBAL", "ILLEGAL_BASIC_STR_CHARS" ], + [ + "STORE_FAST", + "error_on" + ], [ "LOAD_GLOBAL", "parse_basic_str_escape" ], + [ + "STORE_FAST", + "parse_escapes" + ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "src" @@ -3935,6 +5031,10 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" @@ -4047,6 +5147,14 @@ "BINARY_ADD", "result + src[start_pos:pos]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "char" @@ -4055,6 +5163,10 @@ "COMPARE_OP", "char == \"\\\\\"" ], + [ + "LOAD_FAST", + "result" + ], [ "LOAD_FAST", "src" @@ -4071,6 +5183,10 @@ "BINARY_SUBSCR", "src[start_pos:pos]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "parse_escapes" @@ -4087,14 +5203,34 @@ "CALL_FUNCTION", "parse_escapes(src, pos)" ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "parsed_escape" + ], + [ + "LOAD_FAST", + "result" + ], [ "LOAD_FAST", "parsed_escape" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "pos" ], + [ + "STORE_FAST", + "start_pos" + ], [ "LOAD_FAST", "char" @@ -4123,10 +5259,22 @@ "LOAD_FAST", "char" ], + [ + "BUILD_STRING", + "f\"Illegal character {char!r}\"" + ], [ "CALL_FUNCTION", "suffixed_err(src, pos, f\"Illegal character {char!r}\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "src" @@ -4139,10 +5287,18 @@ "BINARY_SUBSCR", "src[pos]" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_GLOBAL", "IndexError" ], + [ + "STORE_FAST", + "char" + ], [ "LOAD_FAST", "char" @@ -4395,6 +5551,10 @@ "CALL_METHOD", "RE_DATETIME.match(src, pos)" ], + [ + "STORE_FAST", + "datetime_match" + ], [ "LOAD_FAST", "datetime_match" @@ -4411,6 +5571,10 @@ "CALL_FUNCTION", "match_to_datetime(datetime_match)" ], + [ + "STORE_FAST", + "datetime_obj" + ], [ "LOAD_GLOBAL", "ValueError" @@ -4471,6 +5635,10 @@ "CALL_METHOD", "RE_LOCALTIME.match(src, pos)" ], + [ + "STORE_FAST", + "localtime_match" + ], [ "LOAD_FAST", "localtime_match" @@ -4519,6 +5687,10 @@ "CALL_METHOD", "RE_NUMBER.match(src, pos)" ], + [ + "STORE_FAST", + "number_match" + ], [ "LOAD_FAST", "number_match" @@ -4571,6 +5743,10 @@ "BINARY_SUBSCR", "src[pos : pos + 3]" ], + [ + "STORE_FAST", + "first_three" + ], [ "LOAD_FAST", "first_three" @@ -4619,6 +5795,10 @@ "BINARY_SUBSCR", "src[pos : pos + 4]" ], + [ + "STORE_FAST", + "first_four" + ], [ "LOAD_FAST", "first_four" @@ -4663,6 +5843,10 @@ "CALL_FUNCTION", "suffixed_err(src, pos, \"Invalid value\")" ], + [ + "STORE_FAST", + " def coord_repr(src: str, pos: Pos) -> str:\n if pos >= len(src):\n return \"end of document\"\n line = src.count(\"\\n\", 0, pos) + 1\n if line == 1:\n column = pos + 1\n else:\n column = pos - src.rindex(\"\\n\", 0, pos)\n return f\"line {line}, column {column}\"" + ], [ "LOAD_GLOBAL", "TOMLDecodeError" @@ -4687,6 +5871,10 @@ "CALL_FUNCTION", "coord_repr(src, pos)" ], + [ + "BUILD_STRING", + "f\"{msg} (at {coord_repr(src, pos)})\"" + ], [ "CALL_FUNCTION", "TOMLDecodeError(f\"{msg} (at {coord_repr(src, pos)})\")" @@ -4731,6 +5919,10 @@ "BINARY_ADD", "src.count(\"\\n\", 0, pos) + 1" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "line" @@ -4747,6 +5939,10 @@ "BINARY_ADD", "pos + 1" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "pos" @@ -4771,6 +5967,10 @@ "BINARY_SUBTRACT", "pos - src.rindex(\"\\n\", 0, pos)" ], + [ + "STORE_FAST", + "column" + ], [ "LOAD_FAST", "line" @@ -4779,14 +5979,34 @@ "LOAD_FAST", "column" ], + [ + "BUILD_STRING", + "f\"line {line}, column {column}\"" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], + [ + "COMPARE_OP", + "0 <= codepoint <= 55295" + ], [ "LOAD_FAST", "codepoint" ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], + [ + "COMPARE_OP", + "57344 <= codepoint <= 1114111" + ], [ "LOAD_DEREF", "parse_float" @@ -4803,6 +6023,10 @@ "LOAD_GLOBAL", "float" ], + [ + "STORE_FAST", + " def safe_parse_float(float_str: str) -> Any:\n float_value = parse_float(float_str)\n if isinstance(float_value, (dict, list)):\n raise ValueError(\"parse_float must not return dicts or lists\")\n return float_value" + ], [ "LOAD_FAST", "safe_parse_float" @@ -4819,6 +6043,10 @@ "CALL_FUNCTION", "parse_float(float_str)" ], + [ + "STORE_FAST", + "float_value" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/bird-py-3.10.json b/tests/sample_results/bird-py-3.10.json index 068ee4a..e14f61c 100644 --- a/tests/sample_results/bird-py-3.10.json +++ b/tests/sample_results/bird-py-3.10.json @@ -1,4 +1,20 @@ [ + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +27,302 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "from future.utils import iteritems" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "import typing" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import html" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import traceback" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from threading import Lock" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "import hashlib" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from asttokens import ASTTokens" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from outdated import warn_if_outdated" + ], + [ + "STORE_NAME", + "from cached_property import cached_property" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye import tracer" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye import __version__" + ], + [ + "STORE_NAME", + "from numpy import ndarray" + ], [ "LOAD_NAME", "ImportError" @@ -19,6 +331,22 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], [ "LOAD_NAME", "ImportError" @@ -27,10 +355,30 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -39,6 +387,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_NAME", "warn_if_outdated" @@ -59,10 +415,22 @@ "CALL_FUNCTION", "namedtuple('CodeInfo', 'db_func traced_file arg_names')" ], + [ + "STORE_NAME", + "CodeInfo" + ], [ "LOAD_NAME", "TreeTracerBase" ], + [ + "CALL_FUNCTION", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_NAME", "BirdsEye" @@ -71,6 +439,10 @@ "CALL_FUNCTION", "BirdsEye()" ], + [ + "STORE_NAME", + "eye" + ], [ "LOAD_NAME", "NamedTuple" @@ -95,6 +467,14 @@ "CALL_FUNCTION", "NamedTuple('HTMLPosition', [\n ('index', int),\n ('is_start', bool),\n ('depth', int),\n ('html', str),\n])" ], + [ + "STORE_NAME", + "HTMLPosition" + ], + [ + "STORE_NAME", + "def _deep_dict():\n return defaultdict(_deep_dict)" + ], [ "LOAD_NAME", "eye" @@ -143,10 +523,26 @@ "LOAD_ATTR", "eye.after_stmt.__code__" ], + [ + "STORE_NAME", + "_bad_codes" + ], + [ + "STORE_NAME", + "def _tracing_recursively(frame):\n while frame:\n if frame.f_code in _bad_codes:\n return True\n frame = frame.f_back" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_NAME", "Iterable" @@ -159,10 +555,26 @@ "BINARY_SUBSCR", "Iterable[Iteration]" ], + [ + "CALL_FUNCTION", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "TypeRegistry" @@ -171,10 +583,30 @@ "CALL_FUNCTION", "TypeRegistry()" ], + [ + "STORE_NAME", + "type_registry" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "def _safe_iter(val, f=lambda x: x):\n try:\n for x in f(val):\n yield x\n except:\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -187,6 +619,86 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], + [ + "STORE_NAME", + "def is_interesting_expression(node):\n # type: (ast.AST) -> bool\n \"\"\"\n If this expression has a value that may not be exactly what it looks like,\n return True. Put differently, return False if this is just a literal.\n \"\"\"\n return (isinstance(node, ast.expr) and\n not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node)))))" + ], + [ + "STORE_NAME", + "def is_obvious_builtin(node, value):\n # type: (ast.expr, Any) -> bool\n \"\"\"\n Return True if this node looks like a builtin and it really is\n (i.e. hasn't been shadowed).\n \"\"\"\n # noinspection PyUnresolvedReferences\n builtins = cast(dict, __builtins__)\n return ((isinstance(node, ast.Name) and\n node.id in builtins and\n builtins[node.id] is value) or\n isinstance(node, getattr(ast, 'NameConstant', ())))" + ], + [ + "LOAD_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "LOAD_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "LOAD_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "LOAD_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "\"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )" + ], [ "LOAD_NAME", "cached_property" @@ -195,6 +707,14 @@ "CALL_FUNCTION", "cached_property" ], + [ + "STORE_NAME", + " @cached_property\n def db(self):\n return Database(self._db_uri)" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)" + ], [ "LOAD_NAME", "lru_cache" @@ -207,6 +727,90 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())" + ], + [ + "STORE_NAME", + " def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)" + ], + [ + "STORE_NAME", + " def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True" + ], + [ + "STORE_NAME", + " def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value" + ], + [ + "STORE_NAME", + " def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)" + ], + [ + "STORE_NAME", + " def _call_id(self):\n # type: () -> Text\n return uuid4().hex" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id" + ], + [ + "STORE_NAME", + " def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)" + ], + [ + "STORE_NAME", + " def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)" + ], + [ + "STORE_NAME", + " def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)" + ], + [ + "STORE_NAME", + " def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )" + ], + [ + "STORE_NAME", + " def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" + ], [ "LOAD_NAME", "retry_db" @@ -215,6 +819,22 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_NAME", + " @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id" + ], + [ + "STORE_NAME", + " def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)" + ], + [ + "STORE_NAME", + " def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')" + ], + [ + "STORE_NAME", + " def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_GLOBAL", "super" @@ -343,6 +963,10 @@ "CALL_METHOD", "ast.walk(root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "tracer" @@ -443,6 +1067,10 @@ "CALL_METHOD", "super(BirdsEye, self).compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "ASTTokens" @@ -655,6 +1283,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "enumerate" @@ -667,6 +1299,14 @@ "CALL_FUNCTION", "enumerate(loops)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -687,6 +1327,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "i" @@ -743,6 +1387,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "_tracing_recursively" @@ -935,6 +1583,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "exc_value" @@ -963,6 +1615,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1043,6 +1699,10 @@ "CALL_FUNCTION_KW", "NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_DEREF", "self" @@ -1167,6 +1827,10 @@ "UNARY_NOT", "not isinstance(node.parent.parent, ast.GeneratorExp)" ], + [ + "STORE_FAST", + "is_special_comprehension_iter" + ], [ "LOAD_FAST", "is_special_comprehension_iter" @@ -1231,6 +1895,14 @@ "BINARY_ADD", "node._loops + (node.parent,)" ], + [ + "STORE_DEREF", + "loops" + ], + [ + "STORE_FAST", + " def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item" + ], [ "LOAD_GLOBAL", "ChangeValue" @@ -1251,6 +1923,10 @@ "LOAD_DEREF", "value" ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_DEREF", "self" @@ -1295,6 +1971,10 @@ "CALL_METHOD", "frame_info.inner_calls.pop(node, None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1335,6 +2015,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1343,6 +2027,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1363,6 +2051,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1375,6 +2067,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "iteration" @@ -1407,6 +2103,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1415,6 +2115,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1435,6 +2139,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1463,6 +2171,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "value" @@ -1503,6 +2215,10 @@ "CALL_METHOD", "NodeValue.exception(exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1603,6 +2319,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1615,6 +2335,10 @@ "CALL_METHOD", "NodeValue.covered()" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1683,6 +2407,10 @@ "LOAD_ATTR", "enter_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1731,6 +2459,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_GLOBAL", "get_unfrozen_datetime" @@ -1783,6 +2515,10 @@ "BINARY_SUBSCR", "self._code_infos[frame.f_code]" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1811,6 +2547,10 @@ "CALL_FUNCTION", "isinstance(enter_info.enter_node.parent, ast.Module)" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_FAST", "frame" @@ -1827,6 +2567,10 @@ "CALL_METHOD", "frame.f_locals.copy()" ], + [ + "STORE_DEREF", + "f_locals" + ], [ "LOAD_FAST", "code_info" @@ -1835,6 +2579,10 @@ "LOAD_ATTR", "code_info.arg_names" ], + [ + "CALL_FUNCTION", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], [ "LOAD_DEREF", "f_locals" @@ -1847,10 +2595,18 @@ "CALL_METHOD", "f_locals.items()" ], + [ + "CALL_FUNCTION", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], [ "BINARY_ADD", "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_GLOBAL", "json" @@ -1863,6 +2619,10 @@ "LOAD_FAST", "arguments" ], + [ + "CALL_FUNCTION", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], [ "CALL_METHOD", "json.dumps([[k, cheap_repr(v)] for k, v in arguments])" @@ -1939,6 +2699,10 @@ "CALL_METHOD", "self.stack.get(enter_info.caller_frame)" ], + [ + "STORE_FAST", + "prev" + ], [ "LOAD_FAST", "prev" @@ -1955,6 +2719,10 @@ "CALL_FUNCTION", "getattr(prev, 'inner_calls', None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1995,6 +2763,14 @@ "CALL_METHOD", "inner_calls[enter_info.call_node].append(frame_info.call_id)" ], + [ + "LOAD_FAST", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "name" @@ -2019,6 +2795,14 @@ "CALL_METHOD", "f_locals.pop(name)" ], + [ + "LOAD_FAST", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], + [ + "STORE_FAST", + "it" + ], [ "LOAD_FAST", "it" @@ -2032,12 +2816,24 @@ "it[0][0]" ], [ - "COMPARE_OP", - "it[0][0] != '.'" + "COMPARE_OP", + "it[0][0] != '.'" + ], + [ + "LOAD_FAST", + "it" + ], + [ + "LOAD_FAST", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], + [ + "STORE_FAST", + "k" ], [ - "LOAD_FAST", - "it" + "STORE_FAST", + "v" ], [ "LOAD_FAST", @@ -2075,6 +2871,10 @@ "LOAD_ATTR", "exit_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -2123,6 +2923,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_DEREF", + "frame_info" + ], [ "LOAD_DEREF", "frame_info" @@ -2131,6 +2935,10 @@ "LOAD_ATTR", "frame_info.iteration" ], + [ + "STORE_DEREF", + "top_iteration" + ], [ "LOAD_GLOBAL", "_deep_dict" @@ -2139,6 +2947,10 @@ "CALL_FUNCTION", "_deep_dict()" ], + [ + "STORE_DEREF", + "node_values" + ], [ "LOAD_DEREF", "self" @@ -2183,6 +2995,10 @@ "LOAD_ATTR", "self._code_infos[frame.f_code].db_func" ], + [ + "STORE_DEREF", + "db_func" + ], [ "LOAD_DEREF", "exit_info" @@ -2191,6 +3007,10 @@ "LOAD_ATTR", "exit_info.exc_value" ], + [ + "STORE_FAST", + "exc" + ], [ "LOAD_FAST", "exc" @@ -2239,6 +3059,10 @@ "CALL_METHOD", "''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))" ], + [ + "STORE_DEREF", + "traceback_str" + ], [ "LOAD_GLOBAL", "exception_string" @@ -2251,6 +3075,18 @@ "CALL_FUNCTION", "exception_string(exc)" ], + [ + "STORE_DEREF", + "exception" + ], + [ + "STORE_DEREF", + "traceback_str" + ], + [ + "STORE_DEREF", + "exception" + ], [ "LOAD_GLOBAL", "retry_db" @@ -2259,6 +3095,10 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_FAST", + " @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)" + ], [ "LOAD_FAST", "add_call" @@ -2295,6 +3135,10 @@ "LOAD_ATTR", "self.db.Call" ], + [ + "STORE_FAST", + "Call" + ], [ "LOAD_FAST", "Call" @@ -2419,6 +3263,10 @@ "CALL_FUNCTION_KW", "Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +3283,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -2451,6 +3303,10 @@ "CALL_METHOD", "session.add(call)" ], + [ + "CALL_FUNCTION", + " with self.db.session_scope() as session:\n session.add(call)" + ], [ "LOAD_FAST", "iteration" @@ -2467,6 +3323,14 @@ "CALL_METHOD", "iteration.vals.items()" ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_FAST", "tree_index" @@ -2479,10 +3343,18 @@ "BINARY_ADD", "(tree_index,) + path" ], + [ + "STORE_FAST", + "full_path" + ], [ "LOAD_FAST", "node_values" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "full_path" @@ -2491,6 +3363,10 @@ "BINARY_SUBSCR", "full_path[:-1]" ], + [ + "STORE_FAST", + "path_k" + ], [ "LOAD_FAST", "d" @@ -2503,6 +3379,10 @@ "BINARY_SUBSCR", "d[path_k]" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "node_value" @@ -2539,6 +3419,10 @@ "CALL_METHOD", "iteration.loops.values()" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_GLOBAL", "enumerate" @@ -2551,6 +3435,14 @@ "CALL_FUNCTION", "enumerate(loop)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "self" @@ -2611,6 +3503,10 @@ "CALL_METHOD", "super(BirdsEye, self).trace_function(func)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_FAST", "self" @@ -2635,6 +3531,10 @@ "CALL_METHOD", "self._code_infos.get(new_func.__code__)" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_FAST", "code_info" @@ -2659,6 +3559,14 @@ "CALL_METHOD", "inspect.getsourcelines(func)" ], + [ + "STORE_FAST", + "lines" + ], + [ + "STORE_FAST", + "start_lineno" + ], [ "LOAD_FAST", "start_lineno" @@ -2679,6 +3587,10 @@ "BINARY_ADD", "start_lineno + len(lines)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -2691,6 +3603,10 @@ "CALL_FUNCTION", "safe_qualname(func)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "inspect" @@ -2707,6 +3623,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "source_file" + ], [ "LOAD_FAST", "source_file" @@ -2723,6 +3643,10 @@ "LOAD_GLOBAL", "IPYTHON_FILE_PATH" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "os" @@ -2743,6 +3667,10 @@ "CALL_METHOD", "os.path.abspath(source_file)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "new_func" @@ -2751,6 +3679,10 @@ "LOAD_ATTR", "new_func.traced_file" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "inspect" @@ -2771,6 +3703,10 @@ "CALL_METHOD", "inspect.getargs(new_func.__code__)" ], + [ + "STORE_FAST", + "arg_info" + ], [ "LOAD_GLOBAL", "list" @@ -2811,6 +3747,10 @@ "CALL_FUNCTION", "list(chain(flatten_list(arg_info[0]), arg_info[1:]))" ], + [ + "STORE_FAST", + "arg_names" + ], [ "LOAD_FAST", "self" @@ -2859,6 +3799,10 @@ "LOAD_FAST", "new_func" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], [ "LOAD_FAST", "get_ipython" @@ -2867,6 +3811,10 @@ "CALL_FUNCTION", "get_ipython()" ], + [ + "STORE_FAST", + "shell" + ], [ "LOAD_FAST", "shell" @@ -2887,6 +3835,14 @@ "CALL_METHOD", "shell.compile.cache(source)" ], + [ + "STORE_FAST", + "filename" + ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "shell" @@ -2899,6 +3855,10 @@ "LOAD_ATTR", "shell.compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -2923,6 +3883,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_FAST", "traced_file" @@ -2943,6 +3907,10 @@ "LOAD_ATTR", "traced_file.root.body" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -3119,6 +4087,14 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "context" @@ -3135,6 +4111,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "inspect" @@ -3151,6 +4131,10 @@ "CALL_METHOD", "inspect.getsourcefile(frame)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "filename" @@ -3163,6 +4147,14 @@ "LOAD_FAST", "context" ], + [ + "STORE_FAST", + "context" + ], + [ + "LOAD_FAST", + "context" + ], [ "COMPARE_OP", "context >= 0" @@ -3187,6 +4179,10 @@ "CALL_METHOD", "os.path.abspath(filename)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "frame" @@ -3263,6 +4259,10 @@ "CALL_METHOD", "read_source_file(filename).splitlines()" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "frame" @@ -3303,6 +4303,10 @@ "CALL_METHOD", "'\\n'.join(lines)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -3359,10 +4363,18 @@ "LOAD_FAST", "globs" ], + [ + "STORE_FAST", + "globs" + ], [ "LOAD_FAST", "locs" ], + [ + "STORE_FAST", + "locs" + ], [ "LOAD_DEREF", "self" @@ -3383,6 +4395,10 @@ "CALL_METHOD", "self.compile(source, filename)" ], + [ + "STORE_DEREF", + "traced_file" + ], [ "LOAD_FAST", "globs" @@ -3459,6 +4475,18 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "CALL_FUNCTION", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_DEREF", + "nodes_by_lineno" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )" + ], [ "LOAD_DEREF", "find_code" @@ -3499,6 +4527,14 @@ "CALL_FUNCTION", "exec(traced_file.code, globs, locs)" ], + [ + "LOAD_FAST", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3539,6 +4575,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "inspect" @@ -3591,6 +4631,10 @@ "LOAD_ATTR", "code.co_firstlineno" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_DEREF", "nodes_by_lineno" @@ -3607,6 +4651,10 @@ "CALL_METHOD", "nodes_by_lineno.get(lineno)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -3703,6 +4751,10 @@ "BINARY_ADD", "start_lineno + len(source.splitlines())" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "list" @@ -3735,6 +4787,10 @@ "CALL_FUNCTION", "list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))" ], + [ + "STORE_FAST", + "nodes" + ], [ "LOAD_FAST", "self" @@ -3763,6 +4819,10 @@ "CALL_METHOD", "self._nodes_html(nodes, start_lineno, end_lineno, traced_file)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_GLOBAL", "dict" @@ -3771,10 +4831,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], [ "CALL_FUNCTION_KW", "dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )" ], + [ + "STORE_FAST", + "data_dict" + ], [ "LOAD_FAST", "typ" @@ -3791,6 +4859,10 @@ "LOAD_ATTR", "traced_file.tokens" ], + [ + "STORE_FAST", + "tokens" + ], [ "LOAD_GLOBAL", "only" @@ -3799,10 +4871,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], [ "CALL_FUNCTION", "only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" ], + [ + "STORE_FAST", + "func_node" + ], [ "LOAD_GLOBAL", "source_without_decorators" @@ -3819,6 +4899,14 @@ "CALL_FUNCTION", "source_without_decorators(tokens, func_node)" ], + [ + "STORE_FAST", + "func_startpos" + ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "data_dict" @@ -3911,6 +4999,10 @@ "CALL_FUNCTION_KW", "json.dumps(data_dict, sort_keys=True)" ], + [ + "STORE_FAST", + "data" + ], [ "LOAD_FAST", "self" @@ -3951,6 +5043,10 @@ "CALL_METHOD", "self._db_func(data, filename, html_body, name, start_lineno, source, typ)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_GLOBAL", "CodeInfo" @@ -3987,6 +5083,18 @@ "STORE_SUBSCR", "self._code_infos[code]" ], + [ + "LOAD_FAST", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_FAST", "node" @@ -4011,13 +5119,37 @@ "LOAD_ATTR", "node._loops" ], + [ + "CALL_FUNCTION", + "[n._tree_index for n in node._loops]" + ], + [ + "LOAD_FAST", + "[n._tree_index for n in node._loops]" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" ], [ - "LOAD_ATTR", - "n._tree_index" + "LOAD_ATTR", + "n._tree_index" + ], + [ + "LOAD_FAST", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" ], [ "LOAD_GLOBAL", @@ -4071,6 +5203,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "classes" @@ -4087,6 +5235,10 @@ "LOAD_ATTR", "node.target" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -4099,6 +5251,10 @@ "LOAD_ATTR", "node.test" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_FAST", "tokens" @@ -4115,14 +5271,38 @@ "CALL_METHOD", "tokens.get_text_range(target)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start" + ], + [ + "LOAD_FAST", + "end" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -4151,6 +5331,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "tokens" @@ -4167,14 +5363,38 @@ "CALL_METHOD", "tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start" + ], + [ + "LOAD_FAST", + "end" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "start" @@ -4247,6 +5467,10 @@ "CALL_FUNCTION_KW", "dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" ], + [ + "STORE_FAST", + " def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()" + ], [ "LOAD_FAST", "h" @@ -4299,6 +5523,10 @@ "CALL_FUNCTION", "h(filename + name + html_body + data + str(start_lineno))" ], + [ + "STORE_FAST", + "function_hash" + ], [ "LOAD_FAST", "self" @@ -4311,6 +5539,10 @@ "LOAD_ATTR", "self.db.Function" ], + [ + "STORE_FAST", + "Function" + ], [ "LOAD_FAST", "self" @@ -4327,6 +5559,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_GLOBAL", "one_or_none" @@ -4363,6 +5599,10 @@ "CALL_FUNCTION", "one_or_none(session.query(Function).filter_by(hash=function_hash))" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "db_func" @@ -4415,6 +5655,10 @@ "CALL_FUNCTION_KW", "Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "session" @@ -4471,6 +5715,10 @@ "LOAD_ATTR", "db_func.id" ], + [ + "CALL_FUNCTION", + " with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id" + ], [ "LOAD_GLOBAL", "hashlib" @@ -4511,6 +5759,14 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4699,10 +5955,18 @@ "BINARY_SUBSCR", "node.first_token.start[0]" ], + [ + "COMPARE_OP", + "start_lineno <= node.first_token.start[0] <= end_lineno" + ], [ "LOAD_FAST", "end_lineno" ], + [ + "COMPARE_OP", + "start_lineno <= node.first_token.start[0] <= end_lineno" + ], [ "LOAD_FAST", "traced_file" @@ -4723,6 +5987,14 @@ "CALL_METHOD", "traced_file.tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "start" @@ -4731,6 +6003,14 @@ "LOAD_FAST", "end" ], + [ + "COMPARE_OP", + "start == end == 0" + ], + [ + "COMPARE_OP", + "start == end == 0" + ], [ "LOAD_FAST", "node" @@ -4779,6 +6059,10 @@ "CALL_METHOD", "ast.walk(traced_file.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -4795,6 +6079,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -4815,10 +6103,30 @@ "STORE_ATTR", "child._depth" ], + [ + "STORE_FAST", + "positions" + ], [ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "positions" @@ -4903,6 +6211,10 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "[n[0] for n in nodes]" + ], [ "LOAD_FAST", "end_lineno" @@ -4919,6 +6231,10 @@ "CALL_METHOD", "self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_FAST", "positions" @@ -4967,10 +6283,22 @@ "CALL_METHOD", "positions.sort()" ], + [ + "STORE_FAST", + "html_parts" + ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "positions" ], + [ + "STORE_FAST", + "position" + ], [ "LOAD_FAST", "html_parts" @@ -5047,6 +6375,10 @@ "LOAD_ATTR", "position.index" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_METHOD", "''.join" @@ -5059,6 +6391,10 @@ "CALL_METHOD", "''.join(html_parts)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_METHOD", "'\\n'.join" @@ -5099,6 +6435,10 @@ "CALL_METHOD", "'\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_FAST", "html_body" @@ -5111,6 +6451,14 @@ "CALL_METHOD", "html_body.strip('\\n')" ], + [ + "LOAD_FAST", + "[n[0] for n in nodes]" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -5163,6 +6511,14 @@ "CALL_FUNCTION", "group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n )" ], + [ + "STORE_FAST", + "comprehensions" + ], + [ + "STORE_FAST", + " def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]" + ], [ "LOAD_FAST", "comprehensions" @@ -5175,6 +6531,14 @@ "CALL_METHOD", "comprehensions.values()" ], + [ + "STORE_FAST", + "comp_list" + ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_GLOBAL", "sorted" @@ -5187,6 +6551,10 @@ "CALL_FUNCTION_KW", "sorted(comp_list, key=lambda c: c.first_token.startpos)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5247,6 +6615,10 @@ "CALL_FUNCTION", "get_start(comp.parent)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5279,6 +6651,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "get_start" @@ -5291,6 +6667,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5323,10 +6703,22 @@ "CALL_METHOD", "positions.append(HTMLPosition(start, True, 0, '\\n '))" ], + [ + "LOAD_FAST", + "end_lineno" + ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_FAST", "start" ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_FAST", "end_lineno" @@ -5423,10 +6815,38 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" ], + [ + "LOAD_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "\"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False" + ], + [ + "STORE_NAME", + " def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_FAST", "self" @@ -5495,6 +6915,22 @@ "CALL_METHOD", "self.loops.items()" ], + [ + "CALL_FUNCTION", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "LOAD_FAST", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "iteration_list" + ], [ "LOAD_FAST", "tree_index" @@ -5503,6 +6939,18 @@ "LOAD_FAST", "iteration_list" ], + [ + "CALL_FUNCTION", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "LOAD_FAST", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "iteration" @@ -5512,8 +6960,48 @@ "iteration.extract_iterations" ], [ - "CALL_METHOD", - "iteration.extract_iterations()" + "CALL_METHOD", + "iteration.extract_iterations()" + ], + [ + "LOAD_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "\"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"" + ], + [ + "STORE_NAME", + "side_len" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()" + ], + [ + "STORE_NAME", + " def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1" + ], + [ + "STORE_NAME", + " def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)" + ], + [ + "STORE_NAME", + " def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]" + ], + [ + "STORE_NAME", + " def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" ], [ "LOAD_FAST", @@ -5723,6 +7211,10 @@ "LOAD_FAST", "self" ], + [ + "LOAD_ATTR", + "self.length" + ], [ "STORE_ATTR", "self.length" @@ -5831,10 +7323,26 @@ "LOAD_FAST", "node" ], + [ + "BINARY_SUBSCR", + "self.recorded[node]" + ], [ "STORE_SUBSCR", "self.recorded[node]" ], + [ + "LOAD_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "type" @@ -5859,14 +7367,26 @@ "LOAD_NAME", "complex" ], + [ + "STORE_NAME", + "basic_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "basic_types" + ], [ "LOAD_NAME", "long" ], + [ + "STORE_NAME", + "basic_types" + ], [ "LOAD_NAME", "basic_types" @@ -5899,10 +7419,18 @@ "BINARY_ADD", "basic_types + (list, dict, tuple, set, frozenset, str)" ], + [ + "STORE_NAME", + "special_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "special_types" + ], [ "LOAD_NAME", "PY2" @@ -5915,6 +7443,10 @@ "LOAD_NAME", "bytes" ], + [ + "STORE_NAME", + "special_types" + ], [ "LOAD_NAME", "len" @@ -5927,6 +7459,22 @@ "CALL_FUNCTION", "len(special_types)" ], + [ + "STORE_NAME", + "num_special_types" + ], + [ + "STORE_NAME", + " def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]" + ], + [ + "STORE_NAME", + " def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_GLOBAL", "Lock" @@ -5967,6 +7515,10 @@ "LOAD_ATTR", "self.special_types" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_DEREF", "self" @@ -5983,6 +7535,10 @@ "BINARY_SUBSCR", "self.data[t]" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "len" @@ -6011,6 +7567,10 @@ "CALL_FUNCTION", "correct_type(item)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" @@ -6035,6 +7595,10 @@ "BINARY_SUBSCR", "self.data[t]" ], + [ + "CALL_FUNCTION", + " with self.lock:\n return self.data[t]" + ], [ "LOAD_GLOBAL", "dict" @@ -6055,10 +7619,18 @@ "CALL_METHOD", "self.data.items()" ], + [ + "CALL_FUNCTION", + "((v, k) for k, v in self.data.items())" + ], [ "CALL_FUNCTION", "dict((v, k) for k, v in self.data.items())" ], + [ + "STORE_DEREF", + "rev" + ], [ "LOAD_GLOBAL", "range" @@ -6079,6 +7651,22 @@ "CALL_FUNCTION", "range(len(rev))" ], + [ + "CALL_FUNCTION", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "LOAD_FAST", + "((v, k) for k, v in self.data.items())" + ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "v" @@ -6087,6 +7675,14 @@ "LOAD_FAST", "k" ], + [ + "LOAD_FAST", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -6107,6 +7703,42 @@ "CALL_FUNCTION", "safe_qualname(rev[i])" ], + [ + "LOAD_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "\"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None" + ], + [ + "STORE_NAME", + " def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value" + ], + [ + "STORE_NAME", + " def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))" + ], + [ + "STORE_NAME", + " def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -6115,6 +7747,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)" + ], [ "LOAD_NAME", "classmethod" @@ -6123,6 +7759,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)" + ], [ "LOAD_NAME", "classmethod" @@ -6131,6 +7771,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], [ "LOAD_FAST", "val_repr" @@ -6291,6 +7935,10 @@ "LOAD_ATTR", "self.meta" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -6383,6 +8031,10 @@ "CALL_FUNCTION", "cls(cheap_repr(val), type_registry[val])" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6411,6 +8063,10 @@ "LOAD_FAST", "result" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6439,6 +8095,10 @@ "CALL_FUNCTION", "len(val)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_FAST", "result" @@ -6483,6 +8143,10 @@ "CALL_FUNCTION", "min(level, 2)" ], + [ + "STORE_FAST", + "level" + ], [ "LOAD_GLOBAL", "partial" @@ -6511,6 +8175,10 @@ "CALL_FUNCTION", "partial(result.add_child, samples, level - 1)" ], + [ + "STORE_FAST", + "add_child" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6531,6 +8199,10 @@ "CALL_FUNCTION", "isinstance(val, (Series, ndarray))" ], + [ + "STORE_FAST", + "attrs" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6563,6 +8235,10 @@ "LOAD_FAST", "attrs" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "getattr" @@ -6579,6 +8255,10 @@ "CALL_FUNCTION", "getattr(val, name)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -6631,6 +8311,14 @@ "CALL_FUNCTION", "isinstance(val, Series)" ], + [ + "STORE_FAST", + "sample_type" + ], + [ + "STORE_FAST", + "sample_type" + ], [ "LOAD_FAST", "samples" @@ -6643,6 +8331,10 @@ "BINARY_SUBSCR", "samples[sample_type]" ], + [ + "STORE_FAST", + "samples" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6659,6 +8351,10 @@ "CALL_FUNCTION", "isinstance(val, DataFrame)" ], + [ + "STORE_FAST", + "meta" + ], [ "LOAD_FAST", "result" @@ -6683,6 +8379,10 @@ "BINARY_SUBSCR", "samples['pandas_rows']" ], + [ + "STORE_FAST", + "max_rows" + ], [ "LOAD_FAST", "samples" @@ -6691,6 +8391,10 @@ "BINARY_SUBSCR", "samples['pandas_cols']" ], + [ + "STORE_FAST", + "max_cols" + ], [ "LOAD_FAST", "length" @@ -6731,6 +8435,10 @@ "LOAD_ATTR", "val.columns" ], + [ + "STORE_FAST", + "columns" + ], [ "LOAD_GLOBAL", "len" @@ -6743,6 +8451,10 @@ "CALL_FUNCTION", "len(columns)" ], + [ + "STORE_FAST", + "num_cols" + ], [ "LOAD_FAST", "num_cols" @@ -6799,6 +8511,10 @@ "CALL_FUNCTION", "set(_sample_indices(num_cols, max_cols))" ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_GLOBAL", "enumerate" @@ -6839,6 +8555,18 @@ "CALL_FUNCTION", "enumerate(zip(val.columns.format(sparsify=False),\n val.columns))" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "formatted_name" + ], + [ + "STORE_FAST", + "label" + ], [ "LOAD_FAST", "i" @@ -6915,6 +8643,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['pandas_rows'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -6951,6 +8683,10 @@ "BINARY_SUBSCR", "val.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "val" @@ -6967,6 +8703,10 @@ "BINARY_SUBSCR", "val.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7087,6 +8827,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['list'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -7099,6 +8843,10 @@ "BINARY_SUBSCR", "val[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7171,6 +8919,14 @@ "CALL_FUNCTION", "islice(_safe_iter(val, iteritems), samples['dict'])" ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7223,6 +8979,10 @@ "CALL_FUNCTION", "_safe_iter(val)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_FAST", "samples" @@ -7231,6 +8991,10 @@ "BINARY_SUBSCR", "samples['set']" ], + [ + "STORE_FAST", + "num_items" + ], [ "LOAD_FAST", "length" @@ -7271,6 +9035,10 @@ "CALL_FUNCTION", "islice(vals, num_items)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_GLOBAL", "enumerate" @@ -7283,6 +9051,14 @@ "CALL_FUNCTION", "enumerate(vals)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7315,6 +9091,10 @@ "CALL_FUNCTION", "getattr(val, '__dict__', None)" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "d" @@ -7359,6 +9139,10 @@ "CALL_FUNCTION_KW", "sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str)" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "d" @@ -7371,6 +9155,10 @@ "BINARY_SUBSCR", "d[k]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -7443,6 +9231,10 @@ "CALL_FUNCTION", "sorted(getattr(type(val), '__slots__', None) or ())" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "getattr" @@ -7459,6 +9251,10 @@ "CALL_FUNCTION", "getattr(val, s)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -7507,6 +9303,10 @@ "CALL_FUNCTION", "f(val)" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -7603,6 +9403,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -7635,6 +9439,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -7643,6 +9455,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -7659,6 +9475,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -7695,6 +9515,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -7711,6 +9535,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" @@ -8023,6 +9851,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "CALL_FUNCTION", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], [ "CALL_FUNCTION", "any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" @@ -8035,6 +9867,14 @@ "UNARY_NOT", "not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))))" ], + [ + "LOAD_FAST", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "is_interesting_expression" @@ -8063,6 +9903,10 @@ "CALL_FUNCTION", "cast(dict, __builtins__)" ], + [ + "STORE_FAST", + "builtins" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/bird-py-3.8.json b/tests/sample_results/bird-py-3.8.json index e7f3f22..ea5df1c 100644 --- a/tests/sample_results/bird-py-3.8.json +++ b/tests/sample_results/bird-py-3.8.json @@ -1,4 +1,20 @@ [ + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +27,302 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "from future.utils import iteritems" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "import typing" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import html" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import traceback" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from threading import Lock" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "import hashlib" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from asttokens import ASTTokens" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from outdated import warn_if_outdated" + ], + [ + "STORE_NAME", + "from cached_property import cached_property" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye import tracer" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye import __version__" + ], + [ + "STORE_NAME", + "from numpy import ndarray" + ], [ "LOAD_NAME", "ImportError" @@ -19,6 +331,22 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], [ "LOAD_NAME", "ImportError" @@ -27,10 +355,30 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -39,6 +387,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_NAME", "warn_if_outdated" @@ -59,10 +415,22 @@ "CALL_FUNCTION", "namedtuple('CodeInfo', 'db_func traced_file arg_names')" ], + [ + "STORE_NAME", + "CodeInfo" + ], [ "LOAD_NAME", "TreeTracerBase" ], + [ + "CALL_FUNCTION", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_NAME", "BirdsEye" @@ -71,6 +439,10 @@ "CALL_FUNCTION", "BirdsEye()" ], + [ + "STORE_NAME", + "eye" + ], [ "LOAD_NAME", "NamedTuple" @@ -95,6 +467,14 @@ "CALL_FUNCTION", "NamedTuple('HTMLPosition', [\n ('index', int),\n ('is_start', bool),\n ('depth', int),\n ('html', str),\n])" ], + [ + "STORE_NAME", + "HTMLPosition" + ], + [ + "STORE_NAME", + "def _deep_dict():\n return defaultdict(_deep_dict)" + ], [ "LOAD_NAME", "eye" @@ -143,10 +523,26 @@ "LOAD_ATTR", "eye.after_stmt.__code__" ], + [ + "STORE_NAME", + "_bad_codes" + ], + [ + "STORE_NAME", + "def _tracing_recursively(frame):\n while frame:\n if frame.f_code in _bad_codes:\n return True\n frame = frame.f_back" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_NAME", "Iterable" @@ -159,10 +555,26 @@ "BINARY_SUBSCR", "Iterable[Iteration]" ], + [ + "CALL_FUNCTION", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "TypeRegistry" @@ -171,10 +583,30 @@ "CALL_FUNCTION", "TypeRegistry()" ], + [ + "STORE_NAME", + "type_registry" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "def _safe_iter(val, f=lambda x: x):\n try:\n for x in f(val):\n yield x\n except:\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -187,6 +619,86 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], + [ + "STORE_NAME", + "def is_interesting_expression(node):\n # type: (ast.AST) -> bool\n \"\"\"\n If this expression has a value that may not be exactly what it looks like,\n return True. Put differently, return False if this is just a literal.\n \"\"\"\n return (isinstance(node, ast.expr) and\n not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node)))))" + ], + [ + "STORE_NAME", + "def is_obvious_builtin(node, value):\n # type: (ast.expr, Any) -> bool\n \"\"\"\n Return True if this node looks like a builtin and it really is\n (i.e. hasn't been shadowed).\n \"\"\"\n # noinspection PyUnresolvedReferences\n builtins = cast(dict, __builtins__)\n return ((isinstance(node, ast.Name) and\n node.id in builtins and\n builtins[node.id] is value) or\n isinstance(node, getattr(ast, 'NameConstant', ())))" + ], + [ + "LOAD_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "LOAD_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "LOAD_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "LOAD_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "\"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )" + ], [ "LOAD_NAME", "cached_property" @@ -195,6 +707,14 @@ "CALL_FUNCTION", "cached_property" ], + [ + "STORE_NAME", + " @cached_property\n def db(self):\n return Database(self._db_uri)" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)" + ], [ "LOAD_NAME", "lru_cache" @@ -207,6 +727,90 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())" + ], + [ + "STORE_NAME", + " def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)" + ], + [ + "STORE_NAME", + " def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True" + ], + [ + "STORE_NAME", + " def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value" + ], + [ + "STORE_NAME", + " def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)" + ], + [ + "STORE_NAME", + " def _call_id(self):\n # type: () -> Text\n return uuid4().hex" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id" + ], + [ + "STORE_NAME", + " def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)" + ], + [ + "STORE_NAME", + " def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)" + ], + [ + "STORE_NAME", + " def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)" + ], + [ + "STORE_NAME", + " def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )" + ], + [ + "STORE_NAME", + " def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" + ], [ "LOAD_NAME", "retry_db" @@ -215,6 +819,22 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_NAME", + " @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id" + ], + [ + "STORE_NAME", + " def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)" + ], + [ + "STORE_NAME", + " def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')" + ], + [ + "STORE_NAME", + " def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_GLOBAL", "super" @@ -343,6 +963,10 @@ "CALL_METHOD", "ast.walk(root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "tracer" @@ -443,6 +1067,10 @@ "CALL_METHOD", "super(BirdsEye, self).compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "ASTTokens" @@ -655,6 +1283,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "enumerate" @@ -667,6 +1299,14 @@ "CALL_FUNCTION", "enumerate(loops)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -687,6 +1327,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "i" @@ -743,6 +1387,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "_tracing_recursively" @@ -935,6 +1583,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "exc_value" @@ -963,6 +1615,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1043,6 +1699,10 @@ "CALL_FUNCTION_KW", "NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_DEREF", "self" @@ -1167,6 +1827,10 @@ "UNARY_NOT", "not isinstance(node.parent.parent, ast.GeneratorExp)" ], + [ + "STORE_FAST", + "is_special_comprehension_iter" + ], [ "LOAD_FAST", "is_special_comprehension_iter" @@ -1231,6 +1895,14 @@ "BINARY_ADD", "node._loops + (node.parent,)" ], + [ + "STORE_DEREF", + "loops" + ], + [ + "STORE_FAST", + " def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item" + ], [ "LOAD_GLOBAL", "ChangeValue" @@ -1251,6 +1923,10 @@ "LOAD_DEREF", "value" ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_DEREF", "self" @@ -1295,6 +1971,10 @@ "CALL_METHOD", "frame_info.inner_calls.pop(node, None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1335,6 +2015,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1343,6 +2027,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1363,6 +2051,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1375,6 +2067,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "iteration" @@ -1407,6 +2103,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1415,6 +2115,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1435,6 +2139,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1463,6 +2171,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "value" @@ -1503,6 +2215,10 @@ "CALL_METHOD", "NodeValue.exception(exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1603,6 +2319,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1615,6 +2335,10 @@ "CALL_METHOD", "NodeValue.covered()" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1683,6 +2407,10 @@ "LOAD_ATTR", "enter_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1731,6 +2459,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_GLOBAL", "get_unfrozen_datetime" @@ -1783,6 +2515,10 @@ "BINARY_SUBSCR", "self._code_infos[frame.f_code]" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1811,6 +2547,10 @@ "CALL_FUNCTION", "isinstance(enter_info.enter_node.parent, ast.Module)" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_FAST", "frame" @@ -1827,6 +2567,10 @@ "CALL_METHOD", "frame.f_locals.copy()" ], + [ + "STORE_DEREF", + "f_locals" + ], [ "LOAD_FAST", "code_info" @@ -1835,6 +2579,10 @@ "LOAD_ATTR", "code_info.arg_names" ], + [ + "CALL_FUNCTION", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], [ "LOAD_DEREF", "f_locals" @@ -1847,10 +2595,18 @@ "CALL_METHOD", "f_locals.items()" ], + [ + "CALL_FUNCTION", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], [ "BINARY_ADD", "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_GLOBAL", "json" @@ -1863,6 +2619,10 @@ "LOAD_FAST", "arguments" ], + [ + "CALL_FUNCTION", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], [ "CALL_METHOD", "json.dumps([[k, cheap_repr(v)] for k, v in arguments])" @@ -1939,6 +2699,10 @@ "CALL_METHOD", "self.stack.get(enter_info.caller_frame)" ], + [ + "STORE_FAST", + "prev" + ], [ "LOAD_FAST", "prev" @@ -1955,6 +2719,10 @@ "CALL_FUNCTION", "getattr(prev, 'inner_calls', None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1995,6 +2763,14 @@ "CALL_METHOD", "inner_calls[enter_info.call_node].append(frame_info.call_id)" ], + [ + "LOAD_FAST", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "name" @@ -2019,6 +2795,14 @@ "CALL_METHOD", "f_locals.pop(name)" ], + [ + "LOAD_FAST", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], + [ + "STORE_FAST", + "it" + ], [ "LOAD_FAST", "it" @@ -2039,6 +2823,18 @@ "LOAD_FAST", "it" ], + [ + "LOAD_FAST", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "k" @@ -2075,6 +2871,10 @@ "LOAD_ATTR", "exit_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -2123,6 +2923,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_DEREF", + "frame_info" + ], [ "LOAD_DEREF", "frame_info" @@ -2131,6 +2935,10 @@ "LOAD_ATTR", "frame_info.iteration" ], + [ + "STORE_DEREF", + "top_iteration" + ], [ "LOAD_GLOBAL", "_deep_dict" @@ -2139,6 +2947,10 @@ "CALL_FUNCTION", "_deep_dict()" ], + [ + "STORE_DEREF", + "node_values" + ], [ "LOAD_DEREF", "self" @@ -2183,6 +2995,10 @@ "LOAD_ATTR", "self._code_infos[frame.f_code].db_func" ], + [ + "STORE_DEREF", + "db_func" + ], [ "LOAD_DEREF", "exit_info" @@ -2191,6 +3007,10 @@ "LOAD_ATTR", "exit_info.exc_value" ], + [ + "STORE_FAST", + "exc" + ], [ "LOAD_FAST", "exc" @@ -2239,6 +3059,10 @@ "CALL_METHOD", "''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))" ], + [ + "STORE_DEREF", + "traceback_str" + ], [ "LOAD_GLOBAL", "exception_string" @@ -2251,6 +3075,18 @@ "CALL_FUNCTION", "exception_string(exc)" ], + [ + "STORE_DEREF", + "exception" + ], + [ + "STORE_DEREF", + "traceback_str" + ], + [ + "STORE_DEREF", + "exception" + ], [ "LOAD_GLOBAL", "retry_db" @@ -2259,6 +3095,10 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_FAST", + " @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)" + ], [ "LOAD_FAST", "add_call" @@ -2295,6 +3135,10 @@ "LOAD_ATTR", "self.db.Call" ], + [ + "STORE_FAST", + "Call" + ], [ "LOAD_FAST", "Call" @@ -2419,6 +3263,10 @@ "CALL_FUNCTION_KW", "Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +3283,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -2467,6 +3319,14 @@ "CALL_METHOD", "iteration.vals.items()" ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_FAST", "tree_index" @@ -2479,10 +3339,18 @@ "BINARY_ADD", "(tree_index,) + path" ], + [ + "STORE_FAST", + "full_path" + ], [ "LOAD_FAST", "node_values" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "full_path" @@ -2491,6 +3359,10 @@ "BINARY_SUBSCR", "full_path[:-1]" ], + [ + "STORE_FAST", + "path_k" + ], [ "LOAD_FAST", "d" @@ -2503,6 +3375,10 @@ "BINARY_SUBSCR", "d[path_k]" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "node_value" @@ -2539,6 +3415,10 @@ "CALL_METHOD", "iteration.loops.values()" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_GLOBAL", "enumerate" @@ -2551,6 +3431,14 @@ "CALL_FUNCTION", "enumerate(loop)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "self" @@ -2611,6 +3499,10 @@ "CALL_METHOD", "super(BirdsEye, self).trace_function(func)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_FAST", "self" @@ -2635,6 +3527,10 @@ "CALL_METHOD", "self._code_infos.get(new_func.__code__)" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_FAST", "code_info" @@ -2659,6 +3555,14 @@ "CALL_METHOD", "inspect.getsourcelines(func)" ], + [ + "STORE_FAST", + "lines" + ], + [ + "STORE_FAST", + "start_lineno" + ], [ "LOAD_FAST", "start_lineno" @@ -2679,6 +3583,10 @@ "BINARY_ADD", "start_lineno + len(lines)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -2691,6 +3599,10 @@ "CALL_FUNCTION", "safe_qualname(func)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "inspect" @@ -2707,6 +3619,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "source_file" + ], [ "LOAD_FAST", "source_file" @@ -2723,6 +3639,10 @@ "LOAD_GLOBAL", "IPYTHON_FILE_PATH" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "os" @@ -2743,6 +3663,10 @@ "CALL_METHOD", "os.path.abspath(source_file)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "new_func" @@ -2751,6 +3675,10 @@ "LOAD_ATTR", "new_func.traced_file" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "inspect" @@ -2771,6 +3699,10 @@ "CALL_METHOD", "inspect.getargs(new_func.__code__)" ], + [ + "STORE_FAST", + "arg_info" + ], [ "LOAD_GLOBAL", "list" @@ -2811,6 +3743,10 @@ "CALL_FUNCTION", "list(chain(flatten_list(arg_info[0]), arg_info[1:]))" ], + [ + "STORE_FAST", + "arg_names" + ], [ "LOAD_FAST", "self" @@ -2859,6 +3795,10 @@ "LOAD_FAST", "new_func" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], [ "LOAD_FAST", "get_ipython" @@ -2867,6 +3807,10 @@ "CALL_FUNCTION", "get_ipython()" ], + [ + "STORE_FAST", + "shell" + ], [ "LOAD_FAST", "shell" @@ -2887,6 +3831,14 @@ "CALL_METHOD", "shell.compile.cache(source)" ], + [ + "STORE_FAST", + "filename" + ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "shell" @@ -2899,6 +3851,10 @@ "LOAD_ATTR", "shell.compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -2923,6 +3879,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_FAST", "traced_file" @@ -2943,6 +3903,10 @@ "LOAD_ATTR", "traced_file.root.body" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -3095,6 +4059,14 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "context" @@ -3111,6 +4083,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "inspect" @@ -3127,6 +4103,10 @@ "CALL_METHOD", "inspect.getsourcefile(frame)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "filename" @@ -3135,6 +4115,14 @@ "COMPARE_OP", "filename is not None" ], + [ + "LOAD_FAST", + "context -= 1" + ], + [ + "STORE_FAST", + "context -= 1" + ], [ "LOAD_GLOBAL", "os" @@ -3155,6 +4143,10 @@ "CALL_METHOD", "os.path.abspath(filename)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "frame" @@ -3231,6 +4223,10 @@ "CALL_METHOD", "read_source_file(filename).splitlines()" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "frame" @@ -3271,6 +4267,10 @@ "CALL_METHOD", "'\\n'.join(lines)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -3327,10 +4327,18 @@ "LOAD_FAST", "globs" ], + [ + "STORE_FAST", + "globs" + ], [ "LOAD_FAST", "locs" ], + [ + "STORE_FAST", + "locs" + ], [ "LOAD_DEREF", "self" @@ -3351,6 +4359,10 @@ "CALL_METHOD", "self.compile(source, filename)" ], + [ + "STORE_DEREF", + "traced_file" + ], [ "LOAD_FAST", "globs" @@ -3427,6 +4439,18 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "CALL_FUNCTION", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_DEREF", + "nodes_by_lineno" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )" + ], [ "LOAD_DEREF", "find_code" @@ -3467,6 +4491,14 @@ "CALL_FUNCTION", "exec(traced_file.code, globs, locs)" ], + [ + "LOAD_FAST", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3507,6 +4539,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "inspect" @@ -3559,6 +4595,10 @@ "LOAD_ATTR", "code.co_firstlineno" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_DEREF", "nodes_by_lineno" @@ -3575,6 +4615,10 @@ "CALL_METHOD", "nodes_by_lineno.get(lineno)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -3671,6 +4715,10 @@ "BINARY_ADD", "start_lineno + len(source.splitlines())" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "list" @@ -3703,6 +4751,10 @@ "CALL_FUNCTION", "list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))" ], + [ + "STORE_FAST", + "nodes" + ], [ "LOAD_FAST", "self" @@ -3731,6 +4783,10 @@ "CALL_METHOD", "self._nodes_html(nodes, start_lineno, end_lineno, traced_file)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_GLOBAL", "dict" @@ -3739,10 +4795,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], [ "CALL_FUNCTION_KW", "dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )" ], + [ + "STORE_FAST", + "data_dict" + ], [ "LOAD_FAST", "typ" @@ -3759,6 +4823,10 @@ "LOAD_ATTR", "traced_file.tokens" ], + [ + "STORE_FAST", + "tokens" + ], [ "LOAD_GLOBAL", "only" @@ -3767,10 +4835,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], [ "CALL_FUNCTION", "only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" ], + [ + "STORE_FAST", + "func_node" + ], [ "LOAD_GLOBAL", "source_without_decorators" @@ -3787,6 +4863,14 @@ "CALL_FUNCTION", "source_without_decorators(tokens, func_node)" ], + [ + "STORE_FAST", + "func_startpos" + ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "data_dict" @@ -3879,6 +4963,10 @@ "CALL_FUNCTION_KW", "json.dumps(data_dict, sort_keys=True)" ], + [ + "STORE_FAST", + "data" + ], [ "LOAD_FAST", "self" @@ -3919,6 +5007,10 @@ "CALL_METHOD", "self._db_func(data, filename, html_body, name, start_lineno, source, typ)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_GLOBAL", "CodeInfo" @@ -3952,8 +5044,20 @@ "code" ], [ - "STORE_SUBSCR", - "self._code_infos[code]" + "STORE_SUBSCR", + "self._code_infos[code]" + ], + [ + "LOAD_FAST", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" ], [ "LOAD_FAST", @@ -3979,6 +5083,18 @@ "LOAD_ATTR", "node._loops" ], + [ + "CALL_FUNCTION", + "[n._tree_index for n in node._loops]" + ], + [ + "LOAD_FAST", + "[n._tree_index for n in node._loops]" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -3987,6 +5103,18 @@ "LOAD_ATTR", "n._tree_index" ], + [ + "LOAD_FAST", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4039,6 +5167,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "classes" @@ -4055,6 +5199,10 @@ "LOAD_ATTR", "node.target" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -4067,6 +5215,10 @@ "LOAD_ATTR", "node.test" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_FAST", "tokens" @@ -4083,14 +5235,38 @@ "CALL_METHOD", "tokens.get_text_range(target)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start -= func_start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start -= func_start" + ], + [ + "LOAD_FAST", + "end -= func_start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end -= func_start" + ], [ "LOAD_GLOBAL", "dict" @@ -4119,6 +5295,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "tokens" @@ -4135,14 +5327,38 @@ "CALL_METHOD", "tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start -= func_start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start -= func_start" + ], + [ + "LOAD_FAST", + "end -= func_start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end -= func_start" + ], [ "LOAD_FAST", "start" @@ -4215,6 +5431,10 @@ "CALL_FUNCTION_KW", "dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" ], + [ + "STORE_FAST", + " def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()" + ], [ "LOAD_FAST", "h" @@ -4267,6 +5487,10 @@ "CALL_FUNCTION", "h(filename + name + html_body + data + str(start_lineno))" ], + [ + "STORE_FAST", + "function_hash" + ], [ "LOAD_FAST", "self" @@ -4279,6 +5503,10 @@ "LOAD_ATTR", "self.db.Function" ], + [ + "STORE_FAST", + "Function" + ], [ "LOAD_FAST", "self" @@ -4295,6 +5523,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_GLOBAL", "one_or_none" @@ -4331,6 +5563,10 @@ "CALL_FUNCTION", "one_or_none(session.query(Function).filter_by(hash=function_hash))" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "db_func" @@ -4383,6 +5619,10 @@ "CALL_FUNCTION_KW", "Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "session" @@ -4479,6 +5719,14 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4691,6 +5939,14 @@ "CALL_METHOD", "traced_file.tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "start" @@ -4747,6 +6003,10 @@ "CALL_METHOD", "ast.walk(traced_file.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -4763,6 +6023,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -4783,10 +6047,30 @@ "STORE_ATTR", "child._depth" ], + [ + "STORE_FAST", + "positions" + ], [ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "positions" @@ -4871,6 +6155,10 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "[n[0] for n in nodes]" + ], [ "LOAD_FAST", "end_lineno" @@ -4887,6 +6175,10 @@ "CALL_METHOD", "self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_FAST", "positions" @@ -4935,10 +6227,22 @@ "CALL_METHOD", "positions.sort()" ], + [ + "STORE_FAST", + "html_parts" + ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "positions" ], + [ + "STORE_FAST", + "position" + ], [ "LOAD_FAST", "html_parts" @@ -5015,6 +6319,10 @@ "LOAD_ATTR", "position.index" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_METHOD", "''.join" @@ -5027,6 +6335,10 @@ "CALL_METHOD", "''.join(html_parts)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_METHOD", "'\\n'.join" @@ -5067,6 +6379,10 @@ "CALL_METHOD", "'\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_FAST", "html_body" @@ -5079,6 +6395,14 @@ "CALL_METHOD", "html_body.strip('\\n')" ], + [ + "LOAD_FAST", + "[n[0] for n in nodes]" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -5131,6 +6455,14 @@ "CALL_FUNCTION", "group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n )" ], + [ + "STORE_FAST", + "comprehensions" + ], + [ + "STORE_FAST", + " def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]" + ], [ "LOAD_FAST", "comprehensions" @@ -5143,6 +6475,14 @@ "CALL_METHOD", "comprehensions.values()" ], + [ + "STORE_FAST", + "comp_list" + ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_GLOBAL", "sorted" @@ -5155,6 +6495,10 @@ "CALL_FUNCTION_KW", "sorted(comp_list, key=lambda c: c.first_token.startpos)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5215,6 +6559,10 @@ "CALL_FUNCTION", "get_start(comp.parent)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5247,6 +6595,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "get_start" @@ -5259,6 +6611,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5291,10 +6647,22 @@ "CALL_METHOD", "positions.append(HTMLPosition(start, True, 0, '\\n '))" ], + [ + "LOAD_FAST", + "end_lineno += 1" + ], + [ + "STORE_FAST", + "end_lineno += 1" + ], [ "LOAD_FAST", "start" ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_FAST", "end_lineno" @@ -5391,6 +6759,34 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], + [ + "LOAD_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "\"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False" + ], + [ + "STORE_NAME", + " def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_FAST", "self" @@ -5460,24 +6856,92 @@ "self.loops.items()" ], [ - "LOAD_FAST", - "tree_index" + "CALL_FUNCTION", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "LOAD_FAST", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "iteration_list" + ], + [ + "LOAD_FAST", + "tree_index" + ], + [ + "LOAD_FAST", + "iteration_list" + ], + [ + "CALL_FUNCTION", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "LOAD_FAST", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "STORE_FAST", + "iteration" + ], + [ + "LOAD_FAST", + "iteration" + ], + [ + "LOAD_METHOD", + "iteration.extract_iterations" + ], + [ + "CALL_METHOD", + "iteration.extract_iterations()" + ], + [ + "LOAD_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "\"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"" + ], + [ + "STORE_NAME", + "side_len" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()" ], [ - "LOAD_FAST", - "iteration_list" + "STORE_NAME", + " def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1" ], [ - "LOAD_FAST", - "iteration" + "STORE_NAME", + " def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)" ], [ - "LOAD_METHOD", - "iteration.extract_iterations" + "STORE_NAME", + " def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]" ], [ - "CALL_METHOD", - "iteration.extract_iterations()" + "STORE_NAME", + " def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" ], [ "LOAD_FAST", @@ -5687,6 +7151,10 @@ "LOAD_FAST", "self" ], + [ + "LOAD_ATTR", + "self.length" + ], [ "STORE_ATTR", "self.length" @@ -5795,10 +7263,26 @@ "LOAD_FAST", "node" ], + [ + "BINARY_SUBSCR", + "self.recorded[node]" + ], [ "STORE_SUBSCR", "self.recorded[node]" ], + [ + "LOAD_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "type" @@ -5823,14 +7307,26 @@ "LOAD_NAME", "complex" ], + [ + "STORE_NAME", + "basic_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "basic_types += (long,)" + ], [ "LOAD_NAME", "long" ], + [ + "STORE_NAME", + "basic_types += (long,)" + ], [ "LOAD_NAME", "basic_types" @@ -5863,10 +7359,18 @@ "BINARY_ADD", "basic_types + (list, dict, tuple, set, frozenset, str)" ], + [ + "STORE_NAME", + "special_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "special_types += (unicode if PY2 else bytes,)" + ], [ "LOAD_NAME", "PY2" @@ -5879,6 +7383,10 @@ "LOAD_NAME", "bytes" ], + [ + "STORE_NAME", + "special_types += (unicode if PY2 else bytes,)" + ], [ "LOAD_NAME", "len" @@ -5891,6 +7399,22 @@ "CALL_FUNCTION", "len(special_types)" ], + [ + "STORE_NAME", + "num_special_types" + ], + [ + "STORE_NAME", + " def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]" + ], + [ + "STORE_NAME", + " def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_GLOBAL", "Lock" @@ -5931,6 +7455,10 @@ "LOAD_ATTR", "self.special_types" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_DEREF", "self" @@ -5947,6 +7475,10 @@ "BINARY_SUBSCR", "self.data[t]" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "len" @@ -5975,6 +7507,10 @@ "CALL_FUNCTION", "correct_type(item)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" @@ -6019,10 +7555,18 @@ "CALL_METHOD", "self.data.items()" ], + [ + "CALL_FUNCTION", + "((v, k) for k, v in self.data.items())" + ], [ "CALL_FUNCTION", "dict((v, k) for k, v in self.data.items())" ], + [ + "STORE_DEREF", + "rev" + ], [ "LOAD_GLOBAL", "range" @@ -6043,6 +7587,22 @@ "CALL_FUNCTION", "range(len(rev))" ], + [ + "CALL_FUNCTION", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "LOAD_FAST", + "((v, k) for k, v in self.data.items())" + ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "v" @@ -6051,6 +7611,14 @@ "LOAD_FAST", "k" ], + [ + "LOAD_FAST", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -6071,6 +7639,42 @@ "CALL_FUNCTION", "safe_qualname(rev[i])" ], + [ + "LOAD_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "\"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None" + ], + [ + "STORE_NAME", + " def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value" + ], + [ + "STORE_NAME", + " def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))" + ], + [ + "STORE_NAME", + " def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -6079,6 +7683,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)" + ], [ "LOAD_NAME", "classmethod" @@ -6087,6 +7695,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)" + ], [ "LOAD_NAME", "classmethod" @@ -6095,6 +7707,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], [ "LOAD_FAST", "val_repr" @@ -6255,6 +7871,10 @@ "LOAD_ATTR", "self.meta" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -6347,6 +7967,10 @@ "CALL_FUNCTION", "cls(cheap_repr(val), type_registry[val])" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6375,6 +7999,10 @@ "LOAD_FAST", "result" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6403,6 +8031,10 @@ "CALL_FUNCTION", "len(val)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_FAST", "result" @@ -6447,6 +8079,10 @@ "CALL_FUNCTION", "min(level, 2)" ], + [ + "STORE_FAST", + "level" + ], [ "LOAD_GLOBAL", "partial" @@ -6475,6 +8111,10 @@ "CALL_FUNCTION", "partial(result.add_child, samples, level - 1)" ], + [ + "STORE_FAST", + "add_child" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6495,6 +8135,10 @@ "CALL_FUNCTION", "isinstance(val, (Series, ndarray))" ], + [ + "STORE_FAST", + "attrs" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6527,6 +8171,10 @@ "LOAD_FAST", "attrs" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "getattr" @@ -6543,6 +8191,10 @@ "CALL_FUNCTION", "getattr(val, name)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -6595,6 +8247,14 @@ "CALL_FUNCTION", "isinstance(val, Series)" ], + [ + "STORE_FAST", + "sample_type" + ], + [ + "STORE_FAST", + "sample_type" + ], [ "LOAD_FAST", "samples" @@ -6607,6 +8267,10 @@ "BINARY_SUBSCR", "samples[sample_type]" ], + [ + "STORE_FAST", + "samples" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6623,6 +8287,10 @@ "CALL_FUNCTION", "isinstance(val, DataFrame)" ], + [ + "STORE_FAST", + "meta" + ], [ "LOAD_FAST", "result" @@ -6647,6 +8315,10 @@ "BINARY_SUBSCR", "samples['pandas_rows']" ], + [ + "STORE_FAST", + "max_rows" + ], [ "LOAD_FAST", "samples" @@ -6655,6 +8327,10 @@ "BINARY_SUBSCR", "samples['pandas_cols']" ], + [ + "STORE_FAST", + "max_cols" + ], [ "LOAD_FAST", "length" @@ -6695,6 +8371,10 @@ "LOAD_ATTR", "val.columns" ], + [ + "STORE_FAST", + "columns" + ], [ "LOAD_GLOBAL", "len" @@ -6707,6 +8387,10 @@ "CALL_FUNCTION", "len(columns)" ], + [ + "STORE_FAST", + "num_cols" + ], [ "LOAD_FAST", "num_cols" @@ -6763,6 +8447,10 @@ "CALL_FUNCTION", "set(_sample_indices(num_cols, max_cols))" ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_GLOBAL", "enumerate" @@ -6803,6 +8491,18 @@ "CALL_FUNCTION", "enumerate(zip(val.columns.format(sparsify=False),\n val.columns))" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "formatted_name" + ], + [ + "STORE_FAST", + "label" + ], [ "LOAD_FAST", "i" @@ -6879,6 +8579,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['pandas_rows'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -6915,6 +8619,10 @@ "BINARY_SUBSCR", "val.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "val" @@ -6931,6 +8639,10 @@ "BINARY_SUBSCR", "val.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7051,6 +8763,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['list'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -7063,6 +8779,10 @@ "BINARY_SUBSCR", "val[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7135,6 +8855,14 @@ "CALL_FUNCTION", "islice(_safe_iter(val, iteritems), samples['dict'])" ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7187,6 +8915,10 @@ "CALL_FUNCTION", "_safe_iter(val)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_FAST", "samples" @@ -7195,6 +8927,10 @@ "BINARY_SUBSCR", "samples['set']" ], + [ + "STORE_FAST", + "num_items" + ], [ "LOAD_FAST", "length" @@ -7235,6 +8971,10 @@ "CALL_FUNCTION", "islice(vals, num_items)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_GLOBAL", "enumerate" @@ -7247,6 +8987,14 @@ "CALL_FUNCTION", "enumerate(vals)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7279,6 +9027,10 @@ "CALL_FUNCTION", "getattr(val, '__dict__', None)" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "d" @@ -7323,6 +9075,10 @@ "CALL_FUNCTION_KW", "sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str)" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "d" @@ -7335,6 +9091,10 @@ "BINARY_SUBSCR", "d[k]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -7403,6 +9163,10 @@ "CALL_FUNCTION", "sorted(getattr(type(val), '__slots__', None) or ())" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "getattr" @@ -7419,6 +9183,10 @@ "CALL_FUNCTION", "getattr(val, s)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -7467,6 +9235,10 @@ "CALL_FUNCTION", "f(val)" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -7563,6 +9335,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -7595,6 +9371,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -7603,6 +9387,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -7619,6 +9407,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -7655,6 +9447,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -7671,6 +9467,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" @@ -7983,6 +9783,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "CALL_FUNCTION", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], [ "CALL_FUNCTION", "any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" @@ -7995,6 +9799,14 @@ "UNARY_NOT", "not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))))" ], + [ + "LOAD_FAST", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "is_interesting_expression" @@ -8023,6 +9835,10 @@ "CALL_FUNCTION", "cast(dict, __builtins__)" ], + [ + "STORE_FAST", + "builtins" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/bird-py-3.9.json b/tests/sample_results/bird-py-3.9.json index 295b853..b45990d 100644 --- a/tests/sample_results/bird-py-3.9.json +++ b/tests/sample_results/bird-py-3.9.json @@ -1,4 +1,20 @@ [ + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from __future__ import absolute_import, division, print_function" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +27,302 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "from future.utils import iteritems" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Iterable, Union, cast" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType, ModuleType" + ], + [ + "STORE_NAME", + "import typing" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import html" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import traceback" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from collections import defaultdict, Sequence, Set, Mapping, deque, namedtuple, Counter" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from itertools import chain, islice" + ], + [ + "STORE_NAME", + "from threading import Lock" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "import hashlib" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from asttokens import ASTTokens" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from littleutils import group_by_key_func, only" + ], + [ + "STORE_NAME", + "from outdated import warn_if_outdated" + ], + [ + "STORE_NAME", + "from cached_property import cached_property" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from cheap_repr.utils import safe_qualname, exception_string" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.db import Database, retry_db" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye.tracer import TreeTracerBase, TracedFile, EnterCallInfo, ExitCallInfo, FrameInfo, ChangeValue, Loop" + ], + [ + "STORE_NAME", + "from birdseye import tracer" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye.utils import correct_type, PY3, PY2, one_or_none, \\\n of_type, Deque, Text, flatten_list, lru_cache, ProtocolEncoder, IPYTHON_FILE_PATH, source_without_decorators, \\\n is_future_import, get_unfrozen_datetime, FILE_SENTINEL_NAME, read_source_file" + ], + [ + "STORE_NAME", + "from birdseye import __version__" + ], + [ + "STORE_NAME", + "from numpy import ndarray" + ], [ "LOAD_NAME", "ImportError" @@ -19,6 +331,22 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], + [ + "STORE_NAME", + "from pandas import DataFrame, Series" + ], [ "LOAD_NAME", "ImportError" @@ -27,10 +355,30 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -39,6 +387,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_NAME", "warn_if_outdated" @@ -59,10 +415,22 @@ "CALL_FUNCTION", "namedtuple('CodeInfo', 'db_func traced_file arg_names')" ], + [ + "STORE_NAME", + "CodeInfo" + ], [ "LOAD_NAME", "TreeTracerBase" ], + [ + "CALL_FUNCTION", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_NAME", "BirdsEye" @@ -71,6 +439,10 @@ "CALL_FUNCTION", "BirdsEye()" ], + [ + "STORE_NAME", + "eye" + ], [ "LOAD_NAME", "NamedTuple" @@ -95,6 +467,14 @@ "CALL_FUNCTION", "NamedTuple('HTMLPosition', [\n ('index', int),\n ('is_start', bool),\n ('depth', int),\n ('html', str),\n])" ], + [ + "STORE_NAME", + "HTMLPosition" + ], + [ + "STORE_NAME", + "def _deep_dict():\n return defaultdict(_deep_dict)" + ], [ "LOAD_NAME", "eye" @@ -143,10 +523,26 @@ "LOAD_ATTR", "eye.after_stmt.__code__" ], + [ + "STORE_NAME", + "_bad_codes" + ], + [ + "STORE_NAME", + "def _tracing_recursively(frame):\n while frame:\n if frame.f_code in _bad_codes:\n return True\n frame = frame.f_back" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_NAME", "Iterable" @@ -159,10 +555,26 @@ "BINARY_SUBSCR", "Iterable[Iteration]" ], + [ + "CALL_FUNCTION", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "TypeRegistry" @@ -171,10 +583,30 @@ "CALL_FUNCTION", "TypeRegistry()" ], + [ + "STORE_NAME", + "type_registry" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "def _safe_iter(val, f=lambda x: x):\n try:\n for x in f(val):\n yield x\n except:\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -187,6 +619,86 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], + [ + "STORE_NAME", + "def is_interesting_expression(node):\n # type: (ast.AST) -> bool\n \"\"\"\n If this expression has a value that may not be exactly what it looks like,\n return True. Put differently, return False if this is just a literal.\n \"\"\"\n return (isinstance(node, ast.expr) and\n not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node)))))" + ], + [ + "STORE_NAME", + "def is_obvious_builtin(node, value):\n # type: (ast.expr, Any) -> bool\n \"\"\"\n Return True if this node looks like a builtin and it really is\n (i.e. hasn't been shadowed).\n \"\"\"\n # noinspection PyUnresolvedReferences\n builtins = cast(dict, __builtins__)\n return ((isinstance(node, ast.Name) and\n node.id in builtins and\n builtins[node.id] is value) or\n isinstance(node, getattr(ast, 'NameConstant', ())))" + ], + [ + "LOAD_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "STORE_NAME", + " class ndarray(object):\n pass" + ], + [ + "LOAD_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "STORE_NAME", + " class DataFrame(object):\n pass" + ], + [ + "LOAD_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "STORE_NAME", + " class Series(object):\n pass" + ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "LOAD_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "class BirdsEye(TreeTracerBase):\n \"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"\n\n def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )\n\n @cached_property\n def db(self):\n return Database(self._db_uri)\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)\n\n def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)\n\n def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())\n\n def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)\n\n def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True\n\n def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value\n\n def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)\n\n def _call_id(self):\n # type: () -> Text\n return uuid4().hex\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id\n\n def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)\n\n def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)\n\n def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)\n\n def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )\n\n def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )\n\n @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id\n\n def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)\n\n def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')\n\n def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], + [ + "STORE_NAME", + "\"\"\"\n Decorate functions with an instance of this class to debug them,\n or just use the existing instance `eye`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, num_samples=None):\n \"\"\"\n Set db_uri to specify where the database lives, as an alternative to\n the environment variable BIRDSEYE_DB.\n \"\"\"\n super(BirdsEye, self).__init__()\n self._db_uri = db_uri\n self._code_infos = {} # type: Dict[CodeType, CodeInfo]\n self._last_call_id = None\n self._ipython_cell_value = None\n self.num_samples = num_samples or dict(\n big=dict(\n attributes=50,\n dict=50,\n list=30,\n set=30,\n pandas_rows=20,\n pandas_cols=100,\n ),\n small=dict(\n attributes=50,\n dict=10,\n list=6,\n set=6,\n pandas_rows=6,\n pandas_cols=10,\n ),\n )" + ], [ "LOAD_NAME", "cached_property" @@ -195,6 +707,14 @@ "CALL_FUNCTION", "cached_property" ], + [ + "STORE_NAME", + " @cached_property\n def db(self):\n return Database(self._db_uri)" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> None\n for node in ast.walk(root): # type: ast.AST\n node._loops = tracer.loops(node)\n if isinstance(node, ast.expr):\n node._is_interesting_expression = is_interesting_expression(node)" + ], [ "LOAD_NAME", "lru_cache" @@ -207,6 +727,90 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n traced_file = super(BirdsEye, self).compile(source, filename, flags)\n traced_file.tokens = ASTTokens(source, tree=traced_file.root)\n return traced_file" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n if frame.f_code not in self._code_infos:\n return\n if isinstance(node.parent, ast.For) and node is node.parent.body[0]:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n if isinstance(node.parent, ast.While) and node is node.parent.test:\n self._add_iteration(node._loops, frame)" + ], + [ + "STORE_NAME", + " def _add_iteration(self, loops, frame):\n # type: (typing.Sequence[Loop], FrameType) -> None\n \"\"\"\n Given one or more nested loops, add an iteration for the innermost\n loop (the last in the sequence).\n \"\"\"\n iteration = self.stack[frame].iteration # type: Iteration\n for i, loop_node in enumerate(loops):\n loop = iteration.loops[loop_node._tree_index]\n if i == len(loops) - 1:\n loop.append(Iteration())\n else:\n iteration = loop.last()" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n\n if _tracing_recursively(frame):\n return None\n\n if frame.f_code not in self._code_infos:\n return None\n\n if node._is_interesting_expression:\n # If this is an expression statement and the last statement\n # in the body, the value is returned from the cell magic\n # to be displayed as usual\n if (self._code_infos[frame.f_code].traced_file.is_ipython_cell\n and isinstance(node.parent, ast.Expr)\n and node.parent is node.parent.parent.body[-1]):\n self._ipython_cell_value = value\n\n if is_obvious_builtin(node, self.stack[frame].expression_values[node]):\n return None\n\n frame_info = self.stack[frame]\n if exc_value:\n node_value = self._exception_value(node, frame, exc_value)\n else:\n node_value = NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )\n self._set_node_value(node, frame, node_value)\n self._check_inner_call(frame_info, node, node_value)\n\n # i.e. is `node` the `y` in `[f(x) for x in y]`, making `node.parent` the `for x in y`\n is_special_comprehension_iter = (\n isinstance(node.parent, ast.comprehension) and\n node is node.parent.iter and\n\n # Generators execute in their own time and aren't directly attached to the parent frame\n not isinstance(node.parent.parent, ast.GeneratorExp))\n\n if not is_special_comprehension_iter:\n return None\n\n # Mark `for x in y` as a bit that executed, so it doesn't show as grey\n self._set_node_value(node.parent, frame, NodeValue.covered())\n\n if exc_value:\n return None\n\n # Track each iteration over `y` so that the 'loop' can be stepped through\n loops = node._loops + (node.parent,) # type: Tuple[Loop, ...]\n\n def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item\n\n # This effectively changes to code to `for x in comprehension_iter_proxy()`\n return ChangeValue(comprehension_iter_proxy())" + ], + [ + "STORE_NAME", + " def _check_inner_call(self, frame_info, node, node_value):\n # type: (FrameInfo, Union[ast.stmt, ast.expr], NodeValue) -> None\n inner_calls = frame_info.inner_calls.pop(node, None)\n if inner_calls:\n node_value.set_meta('inner_calls', inner_calls)" + ], + [ + "STORE_NAME", + " def _is_first_loop_iteration(self, node, frame):\n # type: (ast.AST, FrameType) -> bool\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n iteration = loop.last()\n if iteration.index > 0:\n return False\n return True" + ], + [ + "STORE_NAME", + " def _set_node_value(self, node, frame, value):\n # type: (ast.AST, FrameType, NodeValue) -> None\n iteration = self.stack[frame].iteration # type: Iteration\n for loop_node in node._loops: # type: ast.AST\n loop = iteration.loops[loop_node._tree_index]\n loop.recorded_node(node)\n iteration = loop.last()\n iteration.vals[node._tree_index] = value" + ], + [ + "STORE_NAME", + " def _exception_value(self, node, frame, exc_value):\n # type: (Union[ast.expr, ast.stmt], FrameType, BaseException) -> NodeValue\n value = NodeValue.exception(exc_value)\n self._set_node_value(node, frame, value)\n return value" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return None\n if exc_value and node is exc_node:\n value = self._exception_value(node, frame, exc_value)\n else:\n value = NodeValue.covered()\n self._set_node_value(node, frame, value)\n self._check_inner_call(self.stack[frame], node, value)\n return None" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n frame = enter_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n frame_info.start_time = get_unfrozen_datetime()\n frame_info.iteration = Iteration()\n\n code_info = self._code_infos[frame.f_code]\n if isinstance(enter_info.enter_node.parent, ast.Module):\n arguments = []\n else:\n f_locals = frame.f_locals.copy() # type: Dict[str, Any]\n arguments = [(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]\n frame_info.arguments = json.dumps([[k, cheap_repr(v)] for k, v in arguments])\n frame_info.call_id = self._call_id()\n frame_info.inner_calls = defaultdict(list)\n prev = self.stack.get(enter_info.caller_frame)\n if prev:\n inner_calls = getattr(prev, 'inner_calls', None)\n if inner_calls is not None:\n inner_calls[enter_info.call_node].append(frame_info.call_id)" + ], + [ + "STORE_NAME", + " def _call_id(self):\n # type: () -> Text\n return uuid4().hex" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n This is where all the data collected during the call is gathered up\n and sent to the database.\n \"\"\"\n frame = exit_info.current_frame # type: FrameType\n if frame.f_code not in self._code_infos or _tracing_recursively(frame):\n return\n frame_info = self.stack[frame]\n\n top_iteration = frame_info.iteration # type: Iteration\n node_values = _deep_dict()\n self._extract_node_values(top_iteration, (), node_values)\n\n db_func = self._code_infos[frame.f_code].db_func\n exc = exit_info.exc_value # type: Optional[Exception]\n if exc:\n traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))\n exception = exception_string(exc)\n else:\n traceback_str = exception = None\n\n @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)\n\n add_call()\n\n self._last_call_id = frame_info.call_id" + ], + [ + "STORE_NAME", + " def _extract_node_values(self, iteration, path, node_values):\n # type: (Iteration, Tuple[int, ...], dict) -> None\n \"\"\"\n Populates node_values with values inside iteration.\n \"\"\"\n # Each element of `path` is an index of a loop iteration\n # e.g. given the nested loops:\n #\n # for i in [0, 1, 2]:\n # for j in [0, 1, 2, 3]:\n #\n # path may be (i, j) for each of the iterations\n for tree_index, node_value in iteration.vals.items():\n\n # So this `full_path` is a tuple of ints, but the first\n # int has a different meaning from the others\n full_path = (tree_index,) + path\n\n # Given a path (a, b, c) we're making node_values 'contain'\n # this structure:\n # {a: {b: {c: node_value}}}\n d = node_values\n for path_k in full_path[:-1]:\n d = d[path_k]\n d[full_path[-1]] = node_value\n\n for loop in iteration.loops.values():\n for i, iteration in enumerate(loop):\n self._extract_node_values(iteration, path + (i,), node_values)" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n new_func = super(BirdsEye, self).trace_function(func)\n code_info = self._code_infos.get(new_func.__code__)\n if code_info:\n return new_func\n\n lines, start_lineno = inspect.getsourcelines(func) # type: List[Text], int\n end_lineno = start_lineno + len(lines)\n name = safe_qualname(func)\n source_file = inspect.getsourcefile(func)\n if source_file.startswith('= 0:\n frame = frame.f_back\n filename = inspect.getsourcefile(frame)\n if filename is not None:\n context -= 1\n filename = os.path.abspath(filename)\n\n if frame.f_globals.get('__name__') != '__main__':\n if PY3 and self._treetrace_hidden_with_stmt.__name__ not in frame.f_globals:\n raise RuntimeError(\n 'To trace an imported module, you must import birdseye before '\n 'importing that module.')\n return\n\n lines = read_source_file(filename).splitlines()\n lines[:frame.f_lineno] = [''] * frame.f_lineno\n source = '\\n'.join(lines)\n self.exec_string(source, filename, frame.f_globals, frame.f_locals, deep)\n sys.exit(0)" + ], + [ + "STORE_NAME", + " def exec_string(self, source, filename, globs=None, locs=None, deep=False):\n globs = globs or {}\n locs = locs or {}\n\n traced_file = self.compile(source, filename)\n\n globs.update(self._trace_methods_dict(traced_file))\n\n self._trace(FILE_SENTINEL_NAME, filename, traced_file, traced_file.code, 'module', source)\n\n if deep:\n nodes_by_lineno = {\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )\n\n find_code(traced_file.code)\n\n exec(traced_file.code, globs, locs)" + ], + [ + "STORE_NAME", + " def _trace(\n self,\n name,\n filename,\n traced_file,\n code,\n typ,\n source='',\n start_lineno=1,\n end_lineno=None,\n arg_names=(),\n ):\n if not end_lineno:\n end_lineno = start_lineno + len(source.splitlines())\n nodes = list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))\n html_body = self._nodes_html(nodes, start_lineno, end_lineno, traced_file)\n\n data_dict = dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )\n if typ == 'function':\n tokens = traced_file.tokens\n func_node = only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)\n func_startpos, source = source_without_decorators(tokens, func_node)\n # These are for the PyCharm plugin\n data_dict.update(\n node_ranges=list(self._node_ranges(nodes, tokens, func_startpos)),\n loop_ranges=list(self._loop_ranges(nodes, tokens, func_startpos)),\n )\n\n data = json.dumps(data_dict, sort_keys=True)\n db_func = self._db_func(data, filename, html_body, name, start_lineno, source, typ)\n self._code_infos[code] = CodeInfo(db_func, traced_file, arg_names)" + ], + [ + "STORE_NAME", + " def _loop_ranges(self, nodes, tokens, func_start):\n # For a for loop, e.g.\n #\n # for x in y:\n #\n # this yields the range of the target 'x'.\n #\n # For a while loop, e.g.\n #\n # while x < 10:\n #\n # this yields the range of the condition 'x < 10'.\n for node, (classes, _, __) in nodes:\n if 'loop' not in classes:\n continue\n\n try:\n target = node.target # for loop\n except AttributeError:\n target = node.test # while loop\n\n start, end = tokens.get_text_range(target)\n start -= func_start\n end -= func_start\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end\n )" + ], + [ + "STORE_NAME", + " def _node_ranges(self, nodes, tokens, func_start):\n for node, (classes, _, __) in nodes:\n start, end = tokens.get_text_range(node)\n start -= func_start\n end -= func_start\n\n if start < 0:\n assert (end < 0 # nodes before the def, i.e. decorators\n or isinstance(node, ast.FunctionDef))\n continue\n\n yield dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" + ], [ "LOAD_NAME", "retry_db" @@ -215,6 +819,22 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_NAME", + " @retry_db\n def _db_func(self, data, filename, html_body, name, start_lineno, source, typ):\n \"\"\"\n Retrieve the Function object from the database if one exists, or create one.\n \"\"\"\n\n def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()\n\n function_hash = h(filename + name + html_body + data + str(start_lineno))\n\n Function = self.db.Function\n\n with self.db.session_scope() as session:\n db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function]\n if not db_func:\n db_func = Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)\n session.add(db_func)\n session.commit() # ensure .id exists\n assert isinstance(db_func.id, int)\n return db_func.id" + ], + [ + "STORE_NAME", + " def _nodes_of_interest(self, traced_file, start_lineno, end_lineno):\n # type: (TracedFile, int, int) -> Iterator[Tuple[ast.AST, Tuple]]\n \"\"\"\n Nodes that may have a value, show up as a box in the UI, and lie within the\n given line range.\n \"\"\"\n for node in traced_file.nodes:\n classes = []\n\n if (isinstance(node, (ast.While, ast.For, ast.comprehension)) and\n not isinstance(node.parent, ast.GeneratorExp)):\n classes.append('loop')\n if isinstance(node, ast.stmt):\n classes.append('stmt')\n\n if isinstance(node, ast.expr):\n if not node._is_interesting_expression:\n continue\n elif not classes:\n continue\n\n assert isinstance(node, ast.AST)\n\n # In particular FormattedValue is missing this\n if not hasattr(node, 'first_token'):\n continue\n\n if not start_lineno <= node.first_token.start[0] <= end_lineno:\n continue\n\n start, end = traced_file.tokens.get_text_range(node) # type: int, int\n if start == end == 0:\n continue\n\n yield node, (classes, start, end)" + ], + [ + "STORE_NAME", + " def _nodes_html(self, nodes, start_lineno, end_lineno, traced_file):\n # type: (list, int, int, TracedFile) -> str\n \"\"\"\n The algorithm for generating the HTML works as follows. We generate a list\n of HTMLPositions, which are essentially places to insert HTML into the source plus some\n metadata. The order of the fields of HTMLPosition ensure that when the list is sorted,\n the resulting HTML is valid and correct. Specifically, the fields are:\n \n 1. index: the index in the source string where the HTML would be inserted\n 2. is_start: Indicates if this piece of HTML is the start of a tag, rather than the end.\n Ends should appear first, so that the resulting HTML looks like:\n ... ... \n rather than:\n ... ... \n (I think this might actually be unnecessary, since I can't think of any cases of two\n expressions right next to each other with nothing in between)\n 3. depth: the depth of the corresponding node in the AST. We want the start of a tag from\n a node to appear before the start of a tag nested within, e.g. `foo()` should become:\n foo()\n rather than: \n foo()\n 4. html: the actual HTML to insert. Not important for ordering.\n \n Mostly the list contains pairs of HTMLPositions corresponding to AST nodes, one for the\n start and one for the end.\n \n After the list is sorted, the HTML generated is essentially:\n \n source[0:positions[0].index] + positions[0].html + source[positions[0].index:positions[1].index] + positions[1].html + ...\n \"\"\"\n\n traced_file.root._depth = 0\n for node in ast.walk(traced_file.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child._depth = node._depth + 1\n\n positions = [] # type: List[HTMLPosition]\n\n for node, (classes, start, end) in nodes:\n # noinspection PyArgumentList\n positions.extend(map(\n HTMLPosition,\n [start, end],\n [True, False], # is_start\n [node._depth, node._depth],\n ['' % (node._tree_index, ' '.join(classes)),\n '']))\n\n end_lineno = self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)\n\n # This just makes the loop below simpler\n positions.append(HTMLPosition(len(traced_file.source), False, 0, ''))\n\n positions.sort()\n\n html_parts = []\n start = 0\n for position in positions:\n html_parts.append(html.escape(traced_file.source[start:position.index]))\n html_parts.append(position.html)\n start = position.index\n html_body = ''.join(html_parts)\n html_body = '\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])\n\n return html_body.strip('\\n')" + ], + [ + "STORE_NAME", + " def _separate_comprehensions(self, nodes, end_lineno, positions, traced_file):\n # type: (list, int, List[HTMLPosition], TracedFile) -> int\n \"\"\"\n Comprehensions (e.g. list comprehensions) are troublesome because they can\n be navigated like loops, and the buttons for these need to be on separate lines.\n This function inserts newlines to turn:\n\n [x + y for x in range(3) for y in range(5)] and\n [[x + y for x in range(3)] for y in range(5)]\n\n into\n\n [x + y for x in range(3)\n for y in range(5)] and\n [[x + y for x in range(3)]\n for y in range(5)]\n \"\"\"\n\n comprehensions = group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n ) # type: Dict[Any, Iterable[ast.comprehension]]\n\n def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]\n\n for comp_list in comprehensions.values():\n prev_start = None # type: Optional[int]\n for comp in sorted(comp_list, key=lambda c: c.first_token.startpos):\n if isinstance(comp, ast.comprehension) and comp is comp.parent.generators[0]:\n start = get_start(comp.parent)\n if prev_start is not None and start < prev_start:\n start = get_start(comp)\n else:\n start = get_start(comp)\n if prev_start is not None:\n positions.append(HTMLPosition(start, True, 0, '\\n '))\n end_lineno += 1\n prev_start = start\n\n return end_lineno" + ], [ "LOAD_GLOBAL", "super" @@ -343,6 +963,10 @@ "CALL_METHOD", "ast.walk(root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "tracer" @@ -443,6 +1067,10 @@ "CALL_METHOD", "super(BirdsEye, self).compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "ASTTokens" @@ -655,6 +1283,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "enumerate" @@ -667,6 +1299,14 @@ "CALL_FUNCTION", "enumerate(loops)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -687,6 +1327,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "i" @@ -743,6 +1387,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_GLOBAL", "_tracing_recursively" @@ -935,6 +1583,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "exc_value" @@ -963,6 +1615,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1043,6 +1699,10 @@ "CALL_FUNCTION_KW", "NodeValue.expression(\n self.num_samples,\n value,\n level=max(1, 3 - len(node._loops) * (not self._is_first_loop_iteration(node, frame))),\n )" ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_DEREF", "self" @@ -1167,6 +1827,10 @@ "UNARY_NOT", "not isinstance(node.parent.parent, ast.GeneratorExp)" ], + [ + "STORE_FAST", + "is_special_comprehension_iter" + ], [ "LOAD_FAST", "is_special_comprehension_iter" @@ -1231,6 +1895,14 @@ "BINARY_ADD", "node._loops + (node.parent,)" ], + [ + "STORE_DEREF", + "loops" + ], + [ + "STORE_FAST", + " def comprehension_iter_proxy():\n for item in value:\n self._add_iteration(loops, frame)\n yield item" + ], [ "LOAD_GLOBAL", "ChangeValue" @@ -1251,6 +1923,10 @@ "LOAD_DEREF", "value" ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_DEREF", "self" @@ -1295,6 +1971,10 @@ "CALL_METHOD", "frame_info.inner_calls.pop(node, None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1335,6 +2015,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1343,6 +2027,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1363,6 +2051,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1375,6 +2067,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "iteration" @@ -1407,6 +2103,10 @@ "LOAD_ATTR", "self.stack[frame].iteration" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "node" @@ -1415,6 +2115,10 @@ "LOAD_ATTR", "node._loops" ], + [ + "STORE_FAST", + "loop_node" + ], [ "LOAD_FAST", "iteration" @@ -1435,6 +2139,10 @@ "BINARY_SUBSCR", "iteration.loops[loop_node._tree_index]" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_FAST", "loop" @@ -1463,6 +2171,10 @@ "CALL_METHOD", "loop.last()" ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "value" @@ -1503,6 +2215,10 @@ "CALL_METHOD", "NodeValue.exception(exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1603,6 +2319,10 @@ "CALL_METHOD", "self._exception_value(node, frame, exc_value)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_GLOBAL", "NodeValue" @@ -1615,6 +2335,10 @@ "CALL_METHOD", "NodeValue.covered()" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "self" @@ -1683,6 +2407,10 @@ "LOAD_ATTR", "enter_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1731,6 +2459,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_GLOBAL", "get_unfrozen_datetime" @@ -1783,6 +2515,10 @@ "BINARY_SUBSCR", "self._code_infos[frame.f_code]" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1811,6 +2547,10 @@ "CALL_FUNCTION", "isinstance(enter_info.enter_node.parent, ast.Module)" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_FAST", "frame" @@ -1827,6 +2567,10 @@ "CALL_METHOD", "frame.f_locals.copy()" ], + [ + "STORE_DEREF", + "f_locals" + ], [ "LOAD_FAST", "code_info" @@ -1835,6 +2579,10 @@ "LOAD_ATTR", "code_info.arg_names" ], + [ + "CALL_FUNCTION", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], [ "LOAD_DEREF", "f_locals" @@ -1847,10 +2595,18 @@ "CALL_METHOD", "f_locals.items()" ], + [ + "CALL_FUNCTION", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], [ "BINARY_ADD", "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name] + [\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" ], + [ + "STORE_FAST", + "arguments" + ], [ "LOAD_GLOBAL", "json" @@ -1863,6 +2619,10 @@ "LOAD_FAST", "arguments" ], + [ + "CALL_FUNCTION", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], [ "CALL_METHOD", "json.dumps([[k, cheap_repr(v)] for k, v in arguments])" @@ -1939,6 +2699,10 @@ "CALL_METHOD", "self.stack.get(enter_info.caller_frame)" ], + [ + "STORE_FAST", + "prev" + ], [ "LOAD_FAST", "prev" @@ -1955,6 +2719,10 @@ "CALL_FUNCTION", "getattr(prev, 'inner_calls', None)" ], + [ + "STORE_FAST", + "inner_calls" + ], [ "LOAD_FAST", "inner_calls" @@ -1992,8 +2760,16 @@ "frame_info.call_id" ], [ - "CALL_METHOD", - "inner_calls[enter_info.call_node].append(frame_info.call_id)" + "CALL_METHOD", + "inner_calls[enter_info.call_node].append(frame_info.call_id)" + ], + [ + "LOAD_FAST", + "[(name, f_locals.pop(name))\n for name in code_info.arg_names\n if name]" + ], + [ + "STORE_FAST", + "name" ], [ "LOAD_FAST", @@ -2019,6 +2795,14 @@ "CALL_METHOD", "f_locals.pop(name)" ], + [ + "LOAD_FAST", + "[\n\n # Local variables other than actual arguments. These are variables from\n # the enclosing scope. It's handy to treat them like arguments in the UI\n it for it in f_locals.items()\n if it[0][0] != '.' # Appears when using nested tuple arguments\n ]" + ], + [ + "STORE_FAST", + "it" + ], [ "LOAD_FAST", "it" @@ -2039,6 +2823,18 @@ "LOAD_FAST", "it" ], + [ + "LOAD_FAST", + "[[k, cheap_repr(v)] for k, v in arguments]" + ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "k" @@ -2075,6 +2871,10 @@ "LOAD_ATTR", "exit_info.current_frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -2123,6 +2923,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_DEREF", + "frame_info" + ], [ "LOAD_DEREF", "frame_info" @@ -2131,6 +2935,10 @@ "LOAD_ATTR", "frame_info.iteration" ], + [ + "STORE_DEREF", + "top_iteration" + ], [ "LOAD_GLOBAL", "_deep_dict" @@ -2139,6 +2947,10 @@ "CALL_FUNCTION", "_deep_dict()" ], + [ + "STORE_DEREF", + "node_values" + ], [ "LOAD_DEREF", "self" @@ -2183,6 +2995,10 @@ "LOAD_ATTR", "self._code_infos[frame.f_code].db_func" ], + [ + "STORE_DEREF", + "db_func" + ], [ "LOAD_DEREF", "exit_info" @@ -2191,6 +3007,10 @@ "LOAD_ATTR", "exit_info.exc_value" ], + [ + "STORE_FAST", + "exc" + ], [ "LOAD_FAST", "exc" @@ -2239,6 +3059,10 @@ "CALL_METHOD", "''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb))" ], + [ + "STORE_DEREF", + "traceback_str" + ], [ "LOAD_GLOBAL", "exception_string" @@ -2251,6 +3075,18 @@ "CALL_FUNCTION", "exception_string(exc)" ], + [ + "STORE_DEREF", + "exception" + ], + [ + "STORE_DEREF", + "traceback_str" + ], + [ + "STORE_DEREF", + "exception" + ], [ "LOAD_GLOBAL", "retry_db" @@ -2259,6 +3095,10 @@ "CALL_FUNCTION", "retry_db" ], + [ + "STORE_FAST", + " @retry_db\n def add_call():\n Call = self.db.Call\n call = Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)\n with self.db.session_scope() as session:\n session.add(call)" + ], [ "LOAD_FAST", "add_call" @@ -2295,6 +3135,10 @@ "LOAD_ATTR", "self.db.Call" ], + [ + "STORE_FAST", + "Call" + ], [ "LOAD_FAST", "Call" @@ -2419,6 +3263,10 @@ "CALL_FUNCTION_KW", "Call(id=frame_info.call_id,\n function_id=db_func,\n arguments=frame_info.arguments,\n return_value=cheap_repr(exit_info.return_value),\n exception=exception,\n traceback=traceback_str,\n data=json.dumps(\n dict(\n node_values=node_values,\n loop_iterations=top_iteration.extract_iterations()['loops'],\n type_names=type_registry.names(),\n num_special_types=type_registry.num_special_types,\n ),\n cls=ProtocolEncoder,\n separators=(',', ':')\n ),\n start_time=frame_info.start_time)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +3283,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -2467,6 +3319,14 @@ "CALL_METHOD", "iteration.vals.items()" ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "node_value" + ], [ "LOAD_FAST", "tree_index" @@ -2479,10 +3339,18 @@ "BINARY_ADD", "(tree_index,) + path" ], + [ + "STORE_FAST", + "full_path" + ], [ "LOAD_FAST", "node_values" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "full_path" @@ -2491,6 +3359,10 @@ "BINARY_SUBSCR", "full_path[:-1]" ], + [ + "STORE_FAST", + "path_k" + ], [ "LOAD_FAST", "d" @@ -2503,6 +3375,10 @@ "BINARY_SUBSCR", "d[path_k]" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "node_value" @@ -2539,6 +3415,10 @@ "CALL_METHOD", "iteration.loops.values()" ], + [ + "STORE_FAST", + "loop" + ], [ "LOAD_GLOBAL", "enumerate" @@ -2551,6 +3431,14 @@ "CALL_FUNCTION", "enumerate(loop)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "iteration" + ], [ "LOAD_FAST", "self" @@ -2611,6 +3499,10 @@ "CALL_METHOD", "super(BirdsEye, self).trace_function(func)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_FAST", "self" @@ -2635,6 +3527,10 @@ "CALL_METHOD", "self._code_infos.get(new_func.__code__)" ], + [ + "STORE_FAST", + "code_info" + ], [ "LOAD_FAST", "code_info" @@ -2659,6 +3555,14 @@ "CALL_METHOD", "inspect.getsourcelines(func)" ], + [ + "STORE_FAST", + "lines" + ], + [ + "STORE_FAST", + "start_lineno" + ], [ "LOAD_FAST", "start_lineno" @@ -2679,6 +3583,10 @@ "BINARY_ADD", "start_lineno + len(lines)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -2691,6 +3599,10 @@ "CALL_FUNCTION", "safe_qualname(func)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "inspect" @@ -2707,6 +3619,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "source_file" + ], [ "LOAD_FAST", "source_file" @@ -2723,6 +3639,10 @@ "LOAD_GLOBAL", "IPYTHON_FILE_PATH" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "os" @@ -2743,6 +3663,10 @@ "CALL_METHOD", "os.path.abspath(source_file)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "new_func" @@ -2751,6 +3675,10 @@ "LOAD_ATTR", "new_func.traced_file" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_GLOBAL", "inspect" @@ -2771,6 +3699,10 @@ "CALL_METHOD", "inspect.getargs(new_func.__code__)" ], + [ + "STORE_FAST", + "arg_info" + ], [ "LOAD_GLOBAL", "list" @@ -2811,6 +3743,10 @@ "CALL_FUNCTION", "list(chain(flatten_list(arg_info[0]), arg_info[1:]))" ], + [ + "STORE_FAST", + "arg_names" + ], [ "LOAD_FAST", "self" @@ -2859,6 +3795,10 @@ "LOAD_FAST", "new_func" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], [ "LOAD_FAST", "get_ipython" @@ -2867,6 +3807,10 @@ "CALL_FUNCTION", "get_ipython()" ], + [ + "STORE_FAST", + "shell" + ], [ "LOAD_FAST", "shell" @@ -2887,6 +3831,14 @@ "CALL_METHOD", "shell.compile.cache(source)" ], + [ + "STORE_FAST", + "filename" + ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "shell" @@ -2899,6 +3851,10 @@ "LOAD_ATTR", "shell.compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -2923,6 +3879,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_FAST", "traced_file" @@ -2943,6 +3903,10 @@ "LOAD_ATTR", "traced_file.root.body" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -3119,6 +4083,14 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "context" @@ -3135,6 +4107,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "inspect" @@ -3151,6 +4127,10 @@ "CALL_METHOD", "inspect.getsourcefile(frame)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "filename" @@ -3159,6 +4139,14 @@ "IS_OP", "filename is not None" ], + [ + "LOAD_FAST", + "context" + ], + [ + "STORE_FAST", + "context" + ], [ "LOAD_GLOBAL", "os" @@ -3179,6 +4167,10 @@ "CALL_METHOD", "os.path.abspath(filename)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_FAST", "frame" @@ -3255,6 +4247,10 @@ "CALL_METHOD", "read_source_file(filename).splitlines()" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "frame" @@ -3295,6 +4291,10 @@ "CALL_METHOD", "'\\n'.join(lines)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -3351,10 +4351,18 @@ "LOAD_FAST", "globs" ], + [ + "STORE_FAST", + "globs" + ], [ "LOAD_FAST", "locs" ], + [ + "STORE_FAST", + "locs" + ], [ "LOAD_DEREF", "self" @@ -3375,6 +4383,10 @@ "CALL_METHOD", "self.compile(source, filename)" ], + [ + "STORE_DEREF", + "traced_file" + ], [ "LOAD_FAST", "globs" @@ -3451,6 +4463,18 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "CALL_FUNCTION", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_DEREF", + "nodes_by_lineno" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for code in root_code.co_consts: # type: CodeType\n if not inspect.iscode(code) or code.co_name.startswith('<'):\n continue\n\n find_code(code)\n\n lineno = code.co_firstlineno\n node = nodes_by_lineno.get(lineno)\n if not node:\n continue\n\n self._trace(\n code.co_name, filename, traced_file, code,\n typ='function',\n source=source,\n start_lineno=lineno,\n end_lineno=node.last_token.end[0] + 1,\n )" + ], [ "LOAD_DEREF", "find_code" @@ -3491,6 +4515,14 @@ "CALL_FUNCTION", "exec(traced_file.code, globs, locs)" ], + [ + "LOAD_FAST", + "{\n node.lineno: node\n for node in traced_file.nodes\n if isinstance(node, ast.FunctionDef)\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3531,6 +4563,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "inspect" @@ -3583,6 +4619,10 @@ "LOAD_ATTR", "code.co_firstlineno" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_DEREF", "nodes_by_lineno" @@ -3599,6 +4639,10 @@ "CALL_METHOD", "nodes_by_lineno.get(lineno)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -3695,6 +4739,10 @@ "BINARY_ADD", "start_lineno + len(source.splitlines())" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_GLOBAL", "list" @@ -3727,6 +4775,10 @@ "CALL_FUNCTION", "list(self._nodes_of_interest(traced_file, start_lineno, end_lineno))" ], + [ + "STORE_FAST", + "nodes" + ], [ "LOAD_FAST", "self" @@ -3755,6 +4807,10 @@ "CALL_METHOD", "self._nodes_html(nodes, start_lineno, end_lineno, traced_file)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_GLOBAL", "dict" @@ -3763,10 +4819,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], [ "CALL_FUNCTION_KW", "dict(\n # This maps each node to the loops enclosing that node\n node_loops={\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n },\n )" ], + [ + "STORE_FAST", + "data_dict" + ], [ "LOAD_FAST", "typ" @@ -3783,6 +4847,10 @@ "LOAD_ATTR", "traced_file.tokens" ], + [ + "STORE_FAST", + "tokens" + ], [ "LOAD_GLOBAL", "only" @@ -3791,10 +4859,18 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], [ "CALL_FUNCTION", "only(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" ], + [ + "STORE_FAST", + "func_node" + ], [ "LOAD_GLOBAL", "source_without_decorators" @@ -3811,6 +4887,14 @@ "CALL_FUNCTION", "source_without_decorators(tokens, func_node)" ], + [ + "STORE_FAST", + "func_startpos" + ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "data_dict" @@ -3903,6 +4987,10 @@ "CALL_FUNCTION_KW", "json.dumps(data_dict, sort_keys=True)" ], + [ + "STORE_FAST", + "data" + ], [ "LOAD_FAST", "self" @@ -3943,6 +5031,10 @@ "CALL_METHOD", "self._db_func(data, filename, html_body, name, start_lineno, source, typ)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_GLOBAL", "CodeInfo" @@ -3979,6 +5071,18 @@ "STORE_SUBSCR", "self._code_infos[code]" ], + [ + "LOAD_FAST", + "{\n node._tree_index: [n._tree_index for n in node._loops]\n for node, _ in nodes\n if node._loops\n }" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_FAST", "node" @@ -3997,11 +5101,23 @@ ], [ "LOAD_FAST", - "node" + "node" + ], + [ + "LOAD_ATTR", + "node._loops" + ], + [ + "CALL_FUNCTION", + "[n._tree_index for n in node._loops]" + ], + [ + "LOAD_FAST", + "[n._tree_index for n in node._loops]" ], [ - "LOAD_ATTR", - "node._loops" + "STORE_FAST", + "n" ], [ "LOAD_FAST", @@ -4011,6 +5127,18 @@ "LOAD_ATTR", "n._tree_index" ], + [ + "LOAD_FAST", + "(node\n for node, _ in nodes\n if isinstance(node, ast.FunctionDef)\n and node.first_token.start[0] == start_lineno)" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4063,6 +5191,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "classes" @@ -4079,6 +5223,10 @@ "LOAD_ATTR", "node.target" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -4091,6 +5239,10 @@ "LOAD_ATTR", "node.test" ], + [ + "STORE_FAST", + "target" + ], [ "LOAD_FAST", "tokens" @@ -4107,14 +5259,38 @@ "CALL_METHOD", "tokens.get_text_range(target)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start" + ], + [ + "LOAD_FAST", + "end" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -4143,6 +5319,22 @@ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "__" + ], [ "LOAD_FAST", "tokens" @@ -4159,14 +5351,38 @@ "CALL_METHOD", "tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], + [ + "LOAD_FAST", + "start" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "start" + ], + [ + "LOAD_FAST", + "end" + ], [ "LOAD_FAST", "func_start" ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "start" @@ -4239,6 +5455,10 @@ "CALL_FUNCTION_KW", "dict(\n tree_index=node._tree_index,\n start=start,\n end=end,\n depth=node._depth,\n classes=classes,\n )" ], + [ + "STORE_FAST", + " def h(s):\n return hashlib.sha256(s.encode('utf8')).hexdigest()" + ], [ "LOAD_FAST", "h" @@ -4291,6 +5511,10 @@ "CALL_FUNCTION", "h(filename + name + html_body + data + str(start_lineno))" ], + [ + "STORE_FAST", + "function_hash" + ], [ "LOAD_FAST", "self" @@ -4303,6 +5527,10 @@ "LOAD_ATTR", "self.db.Function" ], + [ + "STORE_FAST", + "Function" + ], [ "LOAD_FAST", "self" @@ -4319,6 +5547,10 @@ "CALL_METHOD", "self.db.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_GLOBAL", "one_or_none" @@ -4355,6 +5587,10 @@ "CALL_FUNCTION", "one_or_none(session.query(Function).filter_by(hash=function_hash))" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "db_func" @@ -4407,6 +5643,10 @@ "CALL_FUNCTION_KW", "Function(file=filename,\n name=name,\n type=typ,\n html_body=html_body,\n lineno=start_lineno,\n data=data,\n body_hash=h(source),\n hash=function_hash)" ], + [ + "STORE_FAST", + "db_func" + ], [ "LOAD_FAST", "session" @@ -4503,6 +5743,14 @@ "LOAD_ATTR", "traced_file.nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4715,6 +5963,14 @@ "CALL_METHOD", "traced_file.tokens.get_text_range(node)" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "start" @@ -4771,6 +6027,10 @@ "CALL_METHOD", "ast.walk(traced_file.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -4787,6 +6047,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -4807,10 +6071,30 @@ "STORE_ATTR", "child._depth" ], + [ + "STORE_FAST", + "positions" + ], [ "LOAD_FAST", "nodes" ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "classes" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "positions" @@ -4895,6 +6179,10 @@ "LOAD_FAST", "nodes" ], + [ + "CALL_FUNCTION", + "[n[0] for n in nodes]" + ], [ "LOAD_FAST", "end_lineno" @@ -4911,6 +6199,10 @@ "CALL_METHOD", "self._separate_comprehensions(\n [n[0] for n in nodes],\n end_lineno, positions, traced_file)" ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_FAST", "positions" @@ -4959,10 +6251,22 @@ "CALL_METHOD", "positions.sort()" ], + [ + "STORE_FAST", + "html_parts" + ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "positions" ], + [ + "STORE_FAST", + "position" + ], [ "LOAD_FAST", "html_parts" @@ -5039,6 +6343,10 @@ "LOAD_ATTR", "position.index" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_METHOD", "''.join" @@ -5051,6 +6359,10 @@ "CALL_METHOD", "''.join(html_parts)" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_METHOD", "'\\n'.join" @@ -5091,6 +6403,10 @@ "CALL_METHOD", "'\\n'.join(html_body.split('\\n')[start_lineno - 1:end_lineno - 1])" ], + [ + "STORE_FAST", + "html_body" + ], [ "LOAD_FAST", "html_body" @@ -5103,6 +6419,14 @@ "CALL_METHOD", "html_body.strip('\\n')" ], + [ + "LOAD_FAST", + "[n[0] for n in nodes]" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -5155,6 +6479,14 @@ "CALL_FUNCTION", "group_by_key_func(of_type((ast.comprehension, ast.While, ast.For), nodes),\n lambda c: c.first_token.start[0]\n )" ], + [ + "STORE_FAST", + "comprehensions" + ], + [ + "STORE_FAST", + " def get_start(n):\n # type: (ast.AST) -> int\n return traced_file.tokens.get_text_range(n)[0]" + ], [ "LOAD_FAST", "comprehensions" @@ -5167,6 +6499,14 @@ "CALL_METHOD", "comprehensions.values()" ], + [ + "STORE_FAST", + "comp_list" + ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_GLOBAL", "sorted" @@ -5179,6 +6519,10 @@ "CALL_FUNCTION_KW", "sorted(comp_list, key=lambda c: c.first_token.startpos)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5239,6 +6583,10 @@ "CALL_FUNCTION", "get_start(comp.parent)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5271,6 +6619,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "get_start" @@ -5283,6 +6635,10 @@ "CALL_FUNCTION", "get_start(comp)" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_FAST", "prev_start" @@ -5315,10 +6671,22 @@ "CALL_METHOD", "positions.append(HTMLPosition(start, True, 0, '\\n '))" ], + [ + "LOAD_FAST", + "end_lineno" + ], + [ + "STORE_FAST", + "end_lineno" + ], [ "LOAD_FAST", "start" ], + [ + "STORE_FAST", + "prev_start" + ], [ "LOAD_FAST", "end_lineno" @@ -5415,6 +6783,34 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], + [ + "LOAD_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "class Iteration(object):\n \"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"\n\n def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False\n\n def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], + [ + "STORE_NAME", + "\"\"\"\n Corresponds to an iteration of a loop during a call, OR\n the call itself (FrameInfo.iteration).\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping of nodes (via node._tree_index) to the value of that\n # node in this iteration. Only contains nodes within the corresponding\n # loop or at the top of the function, but not in loops further within\n # (those will be somewhere within self.loops)\n # Therefore those nodes have at most one value.\n self.vals = {} # type: Dict[int, NodeValue]\n\n # Mapping of loop nodes (via node._tree_index) to IterationLists\n # for loops that happened during this iteration\n self.loops = defaultdict(IterationList) # type: Dict[int, IterationList]\n\n # 0-based index of this iteration\n self.index = None # type: int\n self.keep = False" + ], + [ + "STORE_NAME", + " def extract_iterations(self):\n # type: () -> Dict[str, Union[int, Dict]]\n return {\n 'index': self.index,\n 'loops': {\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }\n }" + ], [ "LOAD_FAST", "self" @@ -5483,6 +6879,22 @@ "CALL_METHOD", "self.loops.items()" ], + [ + "CALL_FUNCTION", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "LOAD_FAST", + "{\n tree_index: [iteration.extract_iterations()\n for iteration in iteration_list]\n for tree_index, iteration_list in self.loops.items()\n }" + ], + [ + "STORE_FAST", + "tree_index" + ], + [ + "STORE_FAST", + "iteration_list" + ], [ "LOAD_FAST", "tree_index" @@ -5492,16 +6904,68 @@ "iteration_list" ], [ - "LOAD_FAST", - "iteration" + "CALL_FUNCTION", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "LOAD_FAST", + "[iteration.extract_iterations()\n for iteration in iteration_list]" + ], + [ + "STORE_FAST", + "iteration" + ], + [ + "LOAD_FAST", + "iteration" + ], + [ + "LOAD_METHOD", + "iteration.extract_iterations" + ], + [ + "CALL_METHOD", + "iteration.extract_iterations()" + ], + [ + "LOAD_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "class IterationList(Iterable[Iteration]):\n \"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"\n side_len = 3\n\n def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()\n\n def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1\n\n def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)\n\n def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]\n\n def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" + ], + [ + "STORE_NAME", + "\"\"\"\n A list of Iterations, corresponding to a run of a loop.\n If the loop has many iterations, only contains the first and last few\n and any in the middle where unique nodes had values, so that\n any node which appeared during this loop exists in at least some iterations.\n \"\"\"" + ], + [ + "STORE_NAME", + "side_len" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Contains the first few iterations\n # and any after that have unique nodes in them\n self.start = [] # type: List[Iteration]\n\n # Contains the last few iterations\n self.end = deque(maxlen=self.side_len) # type: Deque[Iteration]\n\n # Total number of iterations in the loop, not all of which\n # are kept\n self.length = 0 # type: int\n\n # Number of times each node has been recorded in this loop\n self.recorded = Counter()" + ], + [ + "STORE_NAME", + " def append(self, iteration):\n # type: (Iteration) -> None\n if self.length < self.side_len:\n self.start.append(iteration)\n else:\n # If self.end is too long, the first element self.end[0]\n # is about to be dropped by the deque. If that iteration\n # should be kept because of some node that was recorded,\n # add it to self.start\n if len(self.end) >= self.side_len and self.end[0].keep:\n self.start.append(self.end[0])\n\n self.end.append(iteration)\n iteration.index = self.length\n self.length += 1" + ], + [ + "STORE_NAME", + " def __iter__(self):\n # type: () -> Iterator[Iteration]\n return chain(self.start, self.end)" ], [ - "LOAD_METHOD", - "iteration.extract_iterations" + "STORE_NAME", + " def last(self):\n # type: () -> Iteration\n if self.end:\n return self.end[-1]\n else:\n return self.start[-1]" ], [ - "CALL_METHOD", - "iteration.extract_iterations()" + "STORE_NAME", + " def recorded_node(self, node):\n # type: (ast.AST) -> None\n if self.recorded[node] >= 2:\n # We've already seen this node enough\n return\n\n # This node is new(ish), make sure we keep this iteration\n self.last().keep = True\n self.recorded[node] += 1" ], [ "LOAD_FAST", @@ -5711,6 +7175,10 @@ "LOAD_FAST", "self" ], + [ + "LOAD_ATTR", + "self.length" + ], [ "STORE_ATTR", "self.length" @@ -5819,10 +7287,26 @@ "LOAD_FAST", "node" ], + [ + "BINARY_SUBSCR", + "self.recorded[node]" + ], [ "STORE_SUBSCR", "self.recorded[node]" ], + [ + "LOAD_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_NAME", + "class TypeRegistry(object):\n basic_types = (type(None), bool, int, float, complex)\n if PY2:\n basic_types += (long,)\n special_types = basic_types + (list, dict, tuple, set, frozenset, str)\n if PY2:\n special_types += (unicode if PY2 else bytes,)\n\n num_special_types = len(special_types)\n\n def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]\n\n def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]\n\n def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_NAME", "type" @@ -5847,14 +7331,26 @@ "LOAD_NAME", "complex" ], + [ + "STORE_NAME", + "basic_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "basic_types" + ], [ "LOAD_NAME", "long" ], + [ + "STORE_NAME", + "basic_types" + ], [ "LOAD_NAME", "basic_types" @@ -5887,10 +7383,18 @@ "BINARY_ADD", "basic_types + (list, dict, tuple, set, frozenset, str)" ], + [ + "STORE_NAME", + "special_types" + ], [ "LOAD_NAME", "PY2" ], + [ + "LOAD_NAME", + "special_types" + ], [ "LOAD_NAME", "PY2" @@ -5903,6 +7407,10 @@ "LOAD_NAME", "bytes" ], + [ + "STORE_NAME", + "special_types" + ], [ "LOAD_NAME", "len" @@ -5915,6 +7423,22 @@ "CALL_FUNCTION", "len(special_types)" ], + [ + "STORE_NAME", + "num_special_types" + ], + [ + "STORE_NAME", + " def __init__(self):\n self.lock = Lock()\n self.data = defaultdict(lambda: len(self.data)) # type: Dict[type, int]\n\n for t in self.special_types:\n _ = self.data[t]" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n t = correct_type(item)\n with self.lock:\n return self.data[t]" + ], + [ + "STORE_NAME", + " def names(self):\n # type: () -> List[str]\n rev = dict((v, k) for k, v in self.data.items())\n return [safe_qualname(rev[i]) for i in range(len(rev))]" + ], [ "LOAD_GLOBAL", "Lock" @@ -5955,6 +7479,10 @@ "LOAD_ATTR", "self.special_types" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_DEREF", "self" @@ -5971,6 +7499,10 @@ "BINARY_SUBSCR", "self.data[t]" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "len" @@ -5999,6 +7531,10 @@ "CALL_FUNCTION", "correct_type(item)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" @@ -6043,10 +7579,18 @@ "CALL_METHOD", "self.data.items()" ], + [ + "CALL_FUNCTION", + "((v, k) for k, v in self.data.items())" + ], [ "CALL_FUNCTION", "dict((v, k) for k, v in self.data.items())" ], + [ + "STORE_DEREF", + "rev" + ], [ "LOAD_GLOBAL", "range" @@ -6067,6 +7611,22 @@ "CALL_FUNCTION", "range(len(rev))" ], + [ + "CALL_FUNCTION", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "LOAD_FAST", + "((v, k) for k, v in self.data.items())" + ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "v" @@ -6075,6 +7635,14 @@ "LOAD_FAST", "k" ], + [ + "LOAD_FAST", + "[safe_qualname(rev[i]) for i in range(len(rev))]" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "safe_qualname" @@ -6095,6 +7663,42 @@ "CALL_FUNCTION", "safe_qualname(rev[i])" ], + [ + "LOAD_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "class NodeValue(object):\n \"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"\n __slots__ = ('val_repr', 'type_index', 'meta', 'children')\n\n def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None # type: Optional[List[Tuple[str, NodeValue]]]\n\n def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value\n\n def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))\n\n def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result\n\n @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)\n\n @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)\n\n @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], + [ + "STORE_NAME", + "\"\"\"\n The 'value' of a node during a particular iteration.\n This can mean different things, see the classmethods.\n Can also contain some metadata, including links to other calls.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, val_repr, type_index):\n self.val_repr = val_repr # type: str\n self.type_index = type_index # type: int\n self.meta = None # type: Optional[Dict[str, Any]]\n self.children = None" + ], + [ + "STORE_NAME", + " def set_meta(self, key, value):\n # type: (str, Any) -> None\n self.meta = self.meta or {}\n self.meta[key] = value" + ], + [ + "STORE_NAME", + " def add_child(self, samples, level, key, value):\n # type: (dict, int, str, Any) -> None\n self.children = self.children or []\n self.children.append((key, NodeValue.expression(samples, value, level)))" + ], + [ + "STORE_NAME", + " def as_json(self):\n result = [self.val_repr, self.type_index, self.meta or {}] # type: list\n if self.children:\n result.extend(self.children)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -6103,6 +7707,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def covered(cls):\n \"\"\"\n Represents a bit of code, usually a statement, that executed successfully but\n doesn't have an actual value.\n \"\"\"\n return cls('', -2)" + ], [ "LOAD_NAME", "classmethod" @@ -6111,6 +7719,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def exception(cls, exc_value):\n \"\"\"\n Means that exc_value was raised by a node when executing, and not any inner node.\n \"\"\"\n return cls(exception_string(exc_value), -1)" + ], [ "LOAD_NAME", "classmethod" @@ -6119,6 +7731,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def expression(cls, samples, val, level):\n # type: (dict, Any, int) -> NodeValue\n \"\"\"\n The value of an expression or one of its children, with attributes,\n dictionary items, etc as children. Has a max depth of `level` levels.\n \"\"\"\n result = cls(cheap_repr(val), type_registry[val])\n if isinstance(val, (TypeRegistry.basic_types, BirdsEye)):\n return result\n\n length = None\n if not isinstance(val, QuerySet): # len triggers a database query\n try:\n length = len(val)\n except:\n pass\n else:\n result.set_meta('len', length)\n\n if isinstance(val, ModuleType):\n level = min(level, 2)\n\n add_child = partial(result.add_child, samples, level - 1)\n\n if isinstance(val, (Series, ndarray)):\n attrs = ['dtype']\n if isinstance(val, ndarray):\n attrs.append('shape')\n for name in attrs:\n try:\n attr = getattr(val, name)\n except AttributeError:\n pass\n else:\n add_child(name, attr)\n\n if level >= 3 or level >= 2 and isinstance(val, Series):\n sample_type = 'big'\n else:\n sample_type = 'small'\n\n samples = samples[sample_type]\n\n # Always expand DataFrames and Series regardless of level to\n # make the table view of DataFrames work\n\n if isinstance(val, DataFrame):\n meta = {}\n result.set_meta('dataframe', meta)\n\n max_rows = samples['pandas_rows']\n max_cols = samples['pandas_cols']\n\n if length > max_rows + 2:\n meta['row_break'] = max_rows // 2\n\n columns = val.columns\n num_cols = len(columns)\n if num_cols > max_cols + 2:\n meta['col_break'] = max_cols // 2\n\n indices = set(_sample_indices(num_cols, max_cols))\n for i, (formatted_name, label) in enumerate(zip(val.columns.format(sparsify=False),\n val.columns)):\n if i in indices:\n add_child(formatted_name, val[label])\n\n return result\n\n if isinstance(val, Series):\n for i in _sample_indices(length, samples['pandas_rows']):\n try:\n k = val.index[i:i + 1].format(sparsify=False)[0]\n v = val.iloc[i]\n except:\n pass\n else:\n add_child(k, v)\n return result\n\n if (level <= 0 or\n isinstance(val,\n (str, bytes, range)\n if PY3 else\n (str, unicode, xrange))):\n return result\n\n if isinstance(val, (Sequence, ndarray)) and length is not None:\n for i in _sample_indices(length, samples['list']):\n try:\n v = val[i]\n except:\n pass\n else:\n add_child(str(i), v)\n\n if isinstance(val, Mapping):\n for k, v in islice(_safe_iter(val, iteritems), samples['dict']):\n add_child(cheap_repr(k), v)\n\n if isinstance(val, Set):\n vals = _safe_iter(val)\n num_items = samples['set']\n if length is None or length > num_items + 2:\n vals = islice(vals, num_items)\n for i, v in enumerate(vals):\n add_child('<%s>' % i, v)\n\n d = getattr(val, '__dict__', None)\n if d:\n for k in sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str):\n v = d[k]\n if isinstance(v, TracedFile):\n continue\n add_child(str(k), v)\n else:\n for s in sorted(getattr(type(val), '__slots__', None) or ()):\n try:\n attr = getattr(val, s)\n except AttributeError:\n pass\n else:\n add_child(str(s), attr)\n return result" + ], [ "LOAD_FAST", "val_repr" @@ -6279,6 +7895,10 @@ "LOAD_ATTR", "self.meta" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -6371,6 +7991,10 @@ "CALL_FUNCTION", "cls(cheap_repr(val), type_registry[val])" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6399,6 +8023,10 @@ "LOAD_FAST", "result" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6427,6 +8055,10 @@ "CALL_FUNCTION", "len(val)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_FAST", "result" @@ -6471,6 +8103,10 @@ "CALL_FUNCTION", "min(level, 2)" ], + [ + "STORE_FAST", + "level" + ], [ "LOAD_GLOBAL", "partial" @@ -6499,6 +8135,10 @@ "CALL_FUNCTION", "partial(result.add_child, samples, level - 1)" ], + [ + "STORE_FAST", + "add_child" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6519,6 +8159,10 @@ "CALL_FUNCTION", "isinstance(val, (Series, ndarray))" ], + [ + "STORE_FAST", + "attrs" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6551,6 +8195,10 @@ "LOAD_FAST", "attrs" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "getattr" @@ -6567,6 +8215,10 @@ "CALL_FUNCTION", "getattr(val, name)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -6619,6 +8271,14 @@ "CALL_FUNCTION", "isinstance(val, Series)" ], + [ + "STORE_FAST", + "sample_type" + ], + [ + "STORE_FAST", + "sample_type" + ], [ "LOAD_FAST", "samples" @@ -6631,6 +8291,10 @@ "BINARY_SUBSCR", "samples[sample_type]" ], + [ + "STORE_FAST", + "samples" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6647,6 +8311,10 @@ "CALL_FUNCTION", "isinstance(val, DataFrame)" ], + [ + "STORE_FAST", + "meta" + ], [ "LOAD_FAST", "result" @@ -6671,6 +8339,10 @@ "BINARY_SUBSCR", "samples['pandas_rows']" ], + [ + "STORE_FAST", + "max_rows" + ], [ "LOAD_FAST", "samples" @@ -6679,6 +8351,10 @@ "BINARY_SUBSCR", "samples['pandas_cols']" ], + [ + "STORE_FAST", + "max_cols" + ], [ "LOAD_FAST", "length" @@ -6719,6 +8395,10 @@ "LOAD_ATTR", "val.columns" ], + [ + "STORE_FAST", + "columns" + ], [ "LOAD_GLOBAL", "len" @@ -6731,6 +8411,10 @@ "CALL_FUNCTION", "len(columns)" ], + [ + "STORE_FAST", + "num_cols" + ], [ "LOAD_FAST", "num_cols" @@ -6787,6 +8471,10 @@ "CALL_FUNCTION", "set(_sample_indices(num_cols, max_cols))" ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_GLOBAL", "enumerate" @@ -6827,6 +8515,18 @@ "CALL_FUNCTION", "enumerate(zip(val.columns.format(sparsify=False),\n val.columns))" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "formatted_name" + ], + [ + "STORE_FAST", + "label" + ], [ "LOAD_FAST", "i" @@ -6903,6 +8603,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['pandas_rows'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -6939,6 +8643,10 @@ "BINARY_SUBSCR", "val.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "val" @@ -6955,6 +8663,10 @@ "BINARY_SUBSCR", "val.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7075,6 +8787,10 @@ "CALL_FUNCTION", "_sample_indices(length, samples['list'])" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "val" @@ -7087,6 +8803,10 @@ "BINARY_SUBSCR", "val[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7159,6 +8879,14 @@ "CALL_FUNCTION", "islice(_safe_iter(val, iteritems), samples['dict'])" ], + [ + "STORE_FAST", + "k" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7211,6 +8939,10 @@ "CALL_FUNCTION", "_safe_iter(val)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_FAST", "samples" @@ -7219,6 +8951,10 @@ "BINARY_SUBSCR", "samples['set']" ], + [ + "STORE_FAST", + "num_items" + ], [ "LOAD_FAST", "length" @@ -7259,6 +8995,10 @@ "CALL_FUNCTION", "islice(vals, num_items)" ], + [ + "STORE_FAST", + "vals" + ], [ "LOAD_GLOBAL", "enumerate" @@ -7271,6 +9011,14 @@ "CALL_FUNCTION", "enumerate(vals)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "add_child" @@ -7303,6 +9051,10 @@ "CALL_FUNCTION", "getattr(val, '__dict__', None)" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "d" @@ -7347,6 +9099,10 @@ "CALL_FUNCTION_KW", "sorted(islice(_safe_iter(d),\n samples['attributes']),\n key=str)" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "d" @@ -7359,6 +9115,10 @@ "BINARY_SUBSCR", "d[k]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -7427,6 +9187,10 @@ "CALL_FUNCTION", "sorted(getattr(type(val), '__slots__', None) or ())" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "getattr" @@ -7443,6 +9207,10 @@ "CALL_FUNCTION", "getattr(val, s)" ], + [ + "STORE_FAST", + "attr" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -7491,6 +9259,10 @@ "CALL_FUNCTION", "f(val)" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -7587,6 +9359,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -7619,6 +9395,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -7627,6 +9411,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -7643,6 +9431,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -7679,6 +9471,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -7695,6 +9491,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" @@ -8007,6 +9807,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "CALL_FUNCTION", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], [ "CALL_FUNCTION", "any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" @@ -8019,6 +9823,14 @@ "UNARY_NOT", "not (isinstance(node, (ast.Num, ast.Str, getattr(ast, 'NameConstant', ()))) or\n isinstance(getattr(node, 'ctx', None),\n (ast.Store, ast.Del)) or\n (isinstance(node, ast.UnaryOp) and\n isinstance(node.op, (ast.UAdd, ast.USub)) and\n isinstance(node.operand, ast.Num)) or\n (isinstance(node, (ast.List, ast.Tuple, ast.Dict)) and\n not any(is_interesting_expression(n) for n in ast.iter_child_nodes(node))))" ], + [ + "LOAD_FAST", + "(is_interesting_expression(n) for n in ast.iter_child_nodes(node))" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "is_interesting_expression" @@ -8047,6 +9859,10 @@ "CALL_FUNCTION", "cast(dict, __builtins__)" ], + [ + "STORE_FAST", + "builtins" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/configuration-py-3.10.json b/tests/sample_results/configuration-py-3.10.json index 8fa3733..4f5dc99 100644 --- a/tests/sample_results/configuration-py-3.10.json +++ b/tests/sample_results/configuration-py-3.10.json @@ -1,4 +1,84 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from io import open" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "import snoop as package" + ], + [ + "STORE_NAME", + "from snoop.formatting import DefaultFormatter" + ], + [ + "STORE_NAME", + "from snoop.pp_module import PP" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "import ctypes" + ], [ "LOAD_NAME", "ctypes" @@ -11,6 +91,10 @@ "LOAD_ATTR", "ctypes.windll.kernel32" ], + [ + "STORE_NAME", + "kernel32" + ], [ "LOAD_NAME", "kernel32" @@ -35,6 +119,10 @@ "CALL_METHOD", "kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "Exception" @@ -51,18 +139,54 @@ "COMPARE_OP", "os.name != 'nt'" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + "def install(\n builtins=True,\n snoop=\"snoop\",\n pp=\"pp\",\n spy=\"spy\",\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n):\n \"\"\"\n Configure output, enable or disable, and add names to builtins. Parameters:\n \n - builtins: set to False to not add any names to builtins,\n so importing will still be required.\n - snoop, pp, and spy: set to other strings \n to choose the names of these functions in builtins\n - `out`: determines the output destination. By default this is stderr. You can also pass:\n - A string or a `Path` object to write to a file at that location. By default this always will append to the file. Pass `overwrite=True` to clear the file initially.\n - Anything with a `write` method, e.g. `sys.stdout` or a file object.\n - Any callable with a single string argument, e.g. `logger.info`.\n - `color`: determines whether the output includes escape characters to display colored text in the console. If you see weird characters in your output, your console doesn't support colors, so pass `color=False`.\n - Code is syntax highlighted using [Pygments](http://pygments.org/), and this argument is passed as the style. You can choose a different color scheme by passing a string naming a style (see [this gallery](https://help.farbox.com/pygments.html)) or a style class. The default style is monokai. \n - By default this parameter is set to `out.isatty()`, which is usually true for stdout and stderr but will be false if they are redirected or piped. Pass `True` or a style if you want to force coloring.\n - To see colors in the PyCharm Run window, edit the Run Configuration and tick \"Emulate terminal in output console\".\n - `prefix`: Pass a string to start all snoop lines with that string so you can grep for them easily.\n - `columns`: This specifies the columns at the start of each output line. You can pass a string with the names of built in columns separated by spaces or commas. These are the available columns:\n - `time`: The current time. This is the only column by default.\n - `thread`: The name of the current thread. \n - `thread_ident`: The [identifier](https://docs.python.org/3/library/threading.html#threading.Thread.ident) of the current thread, in case thread names are not unique.\n - `file`: The filename (not the full path) of the current function.\n - `full_file`: The full path to the file (also shown anyway when the function is called).\n - `function`: The name of the current function.\n - `function_qualname`: The qualified name of the current function.\n \n If you want a custom column, please open an issue to tell me what you're interested in! In the meantime, you can pass a list, where the elements are either strings or callables. The callables should take one argument, which will be an `Event` object. It has attributes `frame`, `event`, and `arg`, as specified in [`sys.settrace()`](https://docs.python.org/3/library/sys.html#sys.settrace), and other attributes which may change. \n \"\"\"\n \n if builtins:\n setattr(builtins_module, snoop, package.snoop)\n setattr(builtins_module, pp, package.pp)\n setattr(builtins_module, spy, package.spy)\n config = Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )\n package.snoop.config = config\n package.pp.config = config\n package.spy.config = config" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "def len_shape_watch(source, value):\n try:\n shape = value.shape\n except Exception:\n pass\n else:\n if not inspect.ismethod(shape):\n return '{}.shape'.format(source), shape\n\n if isinstance(value, QuerySet):\n # Getting the length of a Django queryset evaluates it\n return None\n\n length = len(value)\n if (\n (isinstance(value, six.string_types)\n and length < 50) or\n (isinstance(value, (Mapping, Set, Sequence))\n and length == 0)\n ):\n return None\n\n return 'len({})'.format(source), length" + ], + [ + "STORE_NAME", + "def dtype_watch(source, value):\n dtype = value.dtype\n if not inspect.ismethod(dtype):\n return '{}.dtype'.format(source), dtype" + ], + [ + "STORE_NAME", + "def get_write_function(output, overwrite):\n is_path = (\n isinstance(output, six.string_types)\n or is_pathlike(output)\n )\n if is_path:\n return FileWriter(output, overwrite).write\n elif callable(output):\n write = output\n else:\n def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))\n return write" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_FAST", "builtins" @@ -183,6 +307,10 @@ "CALL_FUNCTION_KW", "Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )" ], + [ + "STORE_FAST", + "config" + ], [ "LOAD_FAST", "config" @@ -231,10 +359,30 @@ "STORE_ATTR", "package.spy.config" ], + [ + "LOAD_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "\"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + " def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], [ "LOAD_GLOBAL", "can_color" @@ -267,6 +415,10 @@ "CALL_FUNCTION", "getattr(out or sys.stderr, 'isatty', lambda: False)" ], + [ + "STORE_FAST", + "isatty" + ], [ "LOAD_GLOBAL", "bool" @@ -283,6 +435,14 @@ "CALL_FUNCTION", "bool(isatty())" ], + [ + "STORE_FAST", + "color" + ], + [ + "STORE_FAST", + "color" + ], [ "LOAD_GLOBAL", "get_write_function" @@ -371,6 +531,14 @@ "LOAD_GLOBAL", "Tracer" ], + [ + "CALL_FUNCTION", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_FAST", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_FAST", "ConfiguredTracer" @@ -491,10 +659,26 @@ "STORE_ATTR", "self.watch_extras" ], + [ + "LOAD_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_CLASSDEREF", "self" ], + [ + "STORE_NAME", + "config" + ], [ "LOAD_FAST", "value" @@ -503,6 +687,10 @@ "LOAD_ATTR", "value.shape" ], + [ + "STORE_FAST", + "shape" + ], [ "LOAD_GLOBAL", "Exception" @@ -567,6 +755,10 @@ "CALL_FUNCTION", "len(value)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -651,6 +843,10 @@ "LOAD_ATTR", "value.dtype" ], + [ + "STORE_FAST", + "dtype" + ], [ "LOAD_GLOBAL", "inspect" @@ -715,6 +911,10 @@ "CALL_FUNCTION", "is_pathlike(output)" ], + [ + "STORE_FAST", + "is_path" + ], [ "LOAD_FAST", "is_path" @@ -755,10 +955,18 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "write" + ], [ "LOAD_FAST", "write" ], + [ + "STORE_FAST", + " def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))" + ], [ "LOAD_FAST", "write" @@ -767,6 +975,10 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -783,6 +995,10 @@ "LOAD_ATTR", "sys.stderr" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -827,6 +1043,26 @@ "CALL_METHOD", "stream.write(shitcode(s))" ], + [ + "LOAD_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + " def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite" + ], + [ + "STORE_NAME", + " def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_GLOBAL", "six" @@ -887,6 +1123,10 @@ "CALL_FUNCTION_KW", "open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8')" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -903,6 +1143,10 @@ "CALL_METHOD", "f.write(s)" ], + [ + "CALL_FUNCTION", + " with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)" + ], [ "LOAD_FAST", "self" diff --git a/tests/sample_results/configuration-py-3.8.json b/tests/sample_results/configuration-py-3.8.json index 24fc2ad..9aeef33 100644 --- a/tests/sample_results/configuration-py-3.8.json +++ b/tests/sample_results/configuration-py-3.8.json @@ -1,4 +1,84 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from io import open" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "import snoop as package" + ], + [ + "STORE_NAME", + "from snoop.formatting import DefaultFormatter" + ], + [ + "STORE_NAME", + "from snoop.pp_module import PP" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "import ctypes" + ], [ "LOAD_NAME", "ctypes" @@ -11,6 +91,10 @@ "LOAD_ATTR", "ctypes.windll.kernel32" ], + [ + "STORE_NAME", + "kernel32" + ], [ "LOAD_NAME", "kernel32" @@ -35,6 +119,10 @@ "CALL_METHOD", "kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "Exception" @@ -51,18 +139,54 @@ "COMPARE_OP", "os.name != 'nt'" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + "def install(\n builtins=True,\n snoop=\"snoop\",\n pp=\"pp\",\n spy=\"spy\",\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n):\n \"\"\"\n Configure output, enable or disable, and add names to builtins. Parameters:\n \n - builtins: set to False to not add any names to builtins,\n so importing will still be required.\n - snoop, pp, and spy: set to other strings \n to choose the names of these functions in builtins\n - `out`: determines the output destination. By default this is stderr. You can also pass:\n - A string or a `Path` object to write to a file at that location. By default this always will append to the file. Pass `overwrite=True` to clear the file initially.\n - Anything with a `write` method, e.g. `sys.stdout` or a file object.\n - Any callable with a single string argument, e.g. `logger.info`.\n - `color`: determines whether the output includes escape characters to display colored text in the console. If you see weird characters in your output, your console doesn't support colors, so pass `color=False`.\n - Code is syntax highlighted using [Pygments](http://pygments.org/), and this argument is passed as the style. You can choose a different color scheme by passing a string naming a style (see [this gallery](https://help.farbox.com/pygments.html)) or a style class. The default style is monokai. \n - By default this parameter is set to `out.isatty()`, which is usually true for stdout and stderr but will be false if they are redirected or piped. Pass `True` or a style if you want to force coloring.\n - To see colors in the PyCharm Run window, edit the Run Configuration and tick \"Emulate terminal in output console\".\n - `prefix`: Pass a string to start all snoop lines with that string so you can grep for them easily.\n - `columns`: This specifies the columns at the start of each output line. You can pass a string with the names of built in columns separated by spaces or commas. These are the available columns:\n - `time`: The current time. This is the only column by default.\n - `thread`: The name of the current thread. \n - `thread_ident`: The [identifier](https://docs.python.org/3/library/threading.html#threading.Thread.ident) of the current thread, in case thread names are not unique.\n - `file`: The filename (not the full path) of the current function.\n - `full_file`: The full path to the file (also shown anyway when the function is called).\n - `function`: The name of the current function.\n - `function_qualname`: The qualified name of the current function.\n \n If you want a custom column, please open an issue to tell me what you're interested in! In the meantime, you can pass a list, where the elements are either strings or callables. The callables should take one argument, which will be an `Event` object. It has attributes `frame`, `event`, and `arg`, as specified in [`sys.settrace()`](https://docs.python.org/3/library/sys.html#sys.settrace), and other attributes which may change. \n \"\"\"\n \n if builtins:\n setattr(builtins_module, snoop, package.snoop)\n setattr(builtins_module, pp, package.pp)\n setattr(builtins_module, spy, package.spy)\n config = Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )\n package.snoop.config = config\n package.pp.config = config\n package.spy.config = config" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "def len_shape_watch(source, value):\n try:\n shape = value.shape\n except Exception:\n pass\n else:\n if not inspect.ismethod(shape):\n return '{}.shape'.format(source), shape\n\n if isinstance(value, QuerySet):\n # Getting the length of a Django queryset evaluates it\n return None\n\n length = len(value)\n if (\n (isinstance(value, six.string_types)\n and length < 50) or\n (isinstance(value, (Mapping, Set, Sequence))\n and length == 0)\n ):\n return None\n\n return 'len({})'.format(source), length" + ], + [ + "STORE_NAME", + "def dtype_watch(source, value):\n dtype = value.dtype\n if not inspect.ismethod(dtype):\n return '{}.dtype'.format(source), dtype" + ], + [ + "STORE_NAME", + "def get_write_function(output, overwrite):\n is_path = (\n isinstance(output, six.string_types)\n or is_pathlike(output)\n )\n if is_path:\n return FileWriter(output, overwrite).write\n elif callable(output):\n write = output\n else:\n def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))\n return write" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_FAST", "builtins" @@ -183,6 +307,10 @@ "CALL_FUNCTION_KW", "Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )" ], + [ + "STORE_FAST", + "config" + ], [ "LOAD_FAST", "config" @@ -231,10 +359,30 @@ "STORE_ATTR", "package.spy.config" ], + [ + "LOAD_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "\"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + " def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], [ "LOAD_GLOBAL", "can_color" @@ -267,6 +415,10 @@ "CALL_FUNCTION", "getattr(out or sys.stderr, 'isatty', lambda: False)" ], + [ + "STORE_FAST", + "isatty" + ], [ "LOAD_GLOBAL", "bool" @@ -283,6 +435,14 @@ "CALL_FUNCTION", "bool(isatty())" ], + [ + "STORE_FAST", + "color" + ], + [ + "STORE_FAST", + "color" + ], [ "LOAD_GLOBAL", "get_write_function" @@ -371,6 +531,14 @@ "LOAD_GLOBAL", "Tracer" ], + [ + "CALL_FUNCTION", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_FAST", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_FAST", "ConfiguredTracer" @@ -491,10 +659,26 @@ "STORE_ATTR", "self.watch_extras" ], + [ + "LOAD_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_CLASSDEREF", "self" ], + [ + "STORE_NAME", + "config" + ], [ "LOAD_FAST", "value" @@ -503,6 +687,10 @@ "LOAD_ATTR", "value.shape" ], + [ + "STORE_FAST", + "shape" + ], [ "LOAD_GLOBAL", "Exception" @@ -567,6 +755,10 @@ "CALL_FUNCTION", "len(value)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -651,6 +843,10 @@ "LOAD_ATTR", "value.dtype" ], + [ + "STORE_FAST", + "dtype" + ], [ "LOAD_GLOBAL", "inspect" @@ -715,6 +911,10 @@ "CALL_FUNCTION", "is_pathlike(output)" ], + [ + "STORE_FAST", + "is_path" + ], [ "LOAD_FAST", "is_path" @@ -755,6 +955,14 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "write" + ], + [ + "STORE_FAST", + " def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))" + ], [ "LOAD_FAST", "write" @@ -763,6 +971,10 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -779,6 +991,10 @@ "LOAD_ATTR", "sys.stderr" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -823,6 +1039,26 @@ "CALL_METHOD", "stream.write(shitcode(s))" ], + [ + "LOAD_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + " def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite" + ], + [ + "STORE_NAME", + " def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_GLOBAL", "six" @@ -883,6 +1119,10 @@ "CALL_FUNCTION_KW", "open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8')" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" diff --git a/tests/sample_results/configuration-py-3.9.json b/tests/sample_results/configuration-py-3.9.json index c6e83b0..d56ea16 100644 --- a/tests/sample_results/configuration-py-3.9.json +++ b/tests/sample_results/configuration-py-3.9.json @@ -1,4 +1,84 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from collections import Set, Mapping, Sequence" + ], + [ + "STORE_NAME", + "from io import open" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "import snoop as package" + ], + [ + "STORE_NAME", + "from snoop.formatting import DefaultFormatter" + ], + [ + "STORE_NAME", + "from snoop.pp_module import PP" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.tracer import Spy, Tracer" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet" + ], + [ + "STORE_NAME", + "import ctypes" + ], [ "LOAD_NAME", "ctypes" @@ -11,6 +91,10 @@ "LOAD_ATTR", "ctypes.windll.kernel32" ], + [ + "STORE_NAME", + "kernel32" + ], [ "LOAD_NAME", "kernel32" @@ -35,6 +119,10 @@ "CALL_METHOD", "kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "Exception" @@ -51,18 +139,54 @@ "COMPARE_OP", "os.name != 'nt'" ], + [ + "STORE_NAME", + "can_color" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + "def install(\n builtins=True,\n snoop=\"snoop\",\n pp=\"pp\",\n spy=\"spy\",\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n):\n \"\"\"\n Configure output, enable or disable, and add names to builtins. Parameters:\n \n - builtins: set to False to not add any names to builtins,\n so importing will still be required.\n - snoop, pp, and spy: set to other strings \n to choose the names of these functions in builtins\n - `out`: determines the output destination. By default this is stderr. You can also pass:\n - A string or a `Path` object to write to a file at that location. By default this always will append to the file. Pass `overwrite=True` to clear the file initially.\n - Anything with a `write` method, e.g. `sys.stdout` or a file object.\n - Any callable with a single string argument, e.g. `logger.info`.\n - `color`: determines whether the output includes escape characters to display colored text in the console. If you see weird characters in your output, your console doesn't support colors, so pass `color=False`.\n - Code is syntax highlighted using [Pygments](http://pygments.org/), and this argument is passed as the style. You can choose a different color scheme by passing a string naming a style (see [this gallery](https://help.farbox.com/pygments.html)) or a style class. The default style is monokai. \n - By default this parameter is set to `out.isatty()`, which is usually true for stdout and stderr but will be false if they are redirected or piped. Pass `True` or a style if you want to force coloring.\n - To see colors in the PyCharm Run window, edit the Run Configuration and tick \"Emulate terminal in output console\".\n - `prefix`: Pass a string to start all snoop lines with that string so you can grep for them easily.\n - `columns`: This specifies the columns at the start of each output line. You can pass a string with the names of built in columns separated by spaces or commas. These are the available columns:\n - `time`: The current time. This is the only column by default.\n - `thread`: The name of the current thread. \n - `thread_ident`: The [identifier](https://docs.python.org/3/library/threading.html#threading.Thread.ident) of the current thread, in case thread names are not unique.\n - `file`: The filename (not the full path) of the current function.\n - `full_file`: The full path to the file (also shown anyway when the function is called).\n - `function`: The name of the current function.\n - `function_qualname`: The qualified name of the current function.\n \n If you want a custom column, please open an issue to tell me what you're interested in! In the meantime, you can pass a list, where the elements are either strings or callables. The callables should take one argument, which will be an `Event` object. It has attributes `frame`, `event`, and `arg`, as specified in [`sys.settrace()`](https://docs.python.org/3/library/sys.html#sys.settrace), and other attributes which may change. \n \"\"\"\n \n if builtins:\n setattr(builtins_module, snoop, package.snoop)\n setattr(builtins_module, pp, package.pp)\n setattr(builtins_module, spy, package.spy)\n config = Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )\n package.snoop.config = config\n package.pp.config = config\n package.spy.config = config" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "def len_shape_watch(source, value):\n try:\n shape = value.shape\n except Exception:\n pass\n else:\n if not inspect.ismethod(shape):\n return '{}.shape'.format(source), shape\n\n if isinstance(value, QuerySet):\n # Getting the length of a Django queryset evaluates it\n return None\n\n length = len(value)\n if (\n (isinstance(value, six.string_types)\n and length < 50) or\n (isinstance(value, (Mapping, Set, Sequence))\n and length == 0)\n ):\n return None\n\n return 'len({})'.format(source), length" + ], + [ + "STORE_NAME", + "def dtype_watch(source, value):\n dtype = value.dtype\n if not inspect.ismethod(dtype):\n return '{}.dtype'.format(source), dtype" + ], + [ + "STORE_NAME", + "def get_write_function(output, overwrite):\n is_path = (\n isinstance(output, six.string_types)\n or is_pathlike(output)\n )\n if is_path:\n return FileWriter(output, overwrite).write\n elif callable(output):\n write = output\n else:\n def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))\n return write" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_FAST", "builtins" @@ -183,6 +307,10 @@ "CALL_FUNCTION_KW", "Config(\n out=out,\n prefix=prefix,\n columns=columns,\n overwrite=overwrite,\n color=color,\n enabled=enabled,\n watch_extras=watch_extras,\n replace_watch_extras=replace_watch_extras,\n formatter_class=formatter_class,\n )" ], + [ + "STORE_FAST", + "config" + ], [ "LOAD_FAST", "config" @@ -231,10 +359,30 @@ "STORE_ATTR", "package.spy.config" ], + [ + "LOAD_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "class Config(object):\n \"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"\n \n def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], + [ + "STORE_NAME", + "\"\"\"\"\n If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.\n \n The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.\n \"\"\"" + ], [ "LOAD_NAME", "DefaultFormatter" ], + [ + "STORE_NAME", + " def __init__(\n self,\n out=None,\n prefix='',\n columns='time',\n overwrite=False,\n color=None,\n enabled=True,\n watch_extras=(),\n replace_watch_extras=None,\n formatter_class=DefaultFormatter,\n ):\n if can_color:\n if color is None:\n isatty = getattr(out or sys.stderr, 'isatty', lambda: False)\n color = bool(isatty())\n else:\n color = False\n\n self.write = get_write_function(out, overwrite)\n self.formatter = formatter_class(prefix, columns, color)\n self.enabled = enabled\n self.pp = PP(self)\n\n class ConfiguredTracer(Tracer):\n config = self\n\n self.snoop = ConfiguredTracer\n self.spy = Spy(self)\n\n self.last_frame = None\n self.thread_local = threading.local()\n\n if replace_watch_extras is not None:\n self.watch_extras = ensure_tuple(replace_watch_extras)\n else:\n self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)" + ], [ "LOAD_GLOBAL", "can_color" @@ -267,6 +415,10 @@ "CALL_FUNCTION", "getattr(out or sys.stderr, 'isatty', lambda: False)" ], + [ + "STORE_FAST", + "isatty" + ], [ "LOAD_GLOBAL", "bool" @@ -283,6 +435,14 @@ "CALL_FUNCTION", "bool(isatty())" ], + [ + "STORE_FAST", + "color" + ], + [ + "STORE_FAST", + "color" + ], [ "LOAD_GLOBAL", "get_write_function" @@ -371,6 +531,14 @@ "LOAD_GLOBAL", "Tracer" ], + [ + "CALL_FUNCTION", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_FAST", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_FAST", "ConfiguredTracer" @@ -491,10 +659,26 @@ "STORE_ATTR", "self.watch_extras" ], + [ + "LOAD_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], + [ + "STORE_NAME", + " class ConfiguredTracer(Tracer):\n config = self" + ], [ "LOAD_CLASSDEREF", "self" ], + [ + "STORE_NAME", + "config" + ], [ "LOAD_FAST", "value" @@ -503,6 +687,10 @@ "LOAD_ATTR", "value.shape" ], + [ + "STORE_FAST", + "shape" + ], [ "LOAD_GLOBAL", "Exception" @@ -567,6 +755,10 @@ "CALL_FUNCTION", "len(value)" ], + [ + "STORE_FAST", + "length" + ], [ "LOAD_GLOBAL", "isinstance" @@ -651,6 +843,10 @@ "LOAD_ATTR", "value.dtype" ], + [ + "STORE_FAST", + "dtype" + ], [ "LOAD_GLOBAL", "inspect" @@ -715,6 +911,10 @@ "CALL_FUNCTION", "is_pathlike(output)" ], + [ + "STORE_FAST", + "is_path" + ], [ "LOAD_FAST", "is_path" @@ -755,6 +955,14 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "write" + ], + [ + "STORE_FAST", + " def write(s):\n stream = output\n\n if stream is None:\n stream = sys.stderr\n\n try:\n stream.write(s)\n except UnicodeEncodeError:\n # God damn Python 2\n stream.write(shitcode(s))" + ], [ "LOAD_FAST", "write" @@ -763,6 +971,10 @@ "LOAD_DEREF", "output" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -779,6 +991,10 @@ "LOAD_ATTR", "sys.stderr" ], + [ + "STORE_FAST", + "stream" + ], [ "LOAD_FAST", "stream" @@ -823,6 +1039,26 @@ "CALL_METHOD", "stream.write(shitcode(s))" ], + [ + "LOAD_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + "class FileWriter(object):\n def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite\n\n def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], + [ + "STORE_NAME", + " def __init__(self, path, overwrite):\n self.path = six.text_type(path)\n self.overwrite = overwrite" + ], + [ + "STORE_NAME", + " def write(self, s):\n with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:\n f.write(s)\n self.overwrite = False" + ], [ "LOAD_GLOBAL", "six" @@ -883,6 +1119,10 @@ "CALL_FUNCTION_KW", "open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8')" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" diff --git a/tests/sample_results/datetime-py-3.10.json b/tests/sample_results/datetime-py-3.10.json index 73c5791..5101b33 100644 --- a/tests/sample_results/datetime-py-3.10.json +++ b/tests/sample_results/datetime-py-3.10.json @@ -1,4 +1,56 @@ [ + [ + "STORE_NAME", + "\"\"\"Concrete date/time and related types.\n\nSee http://www.iana.org/time-zones/repository/tz-link.html for\ntime zone and DST data sources.\n\"\"\"" + ], + [ + "STORE_NAME", + "__all__" + ], + [ + "STORE_NAME", + "import time as _time" + ], + [ + "STORE_NAME", + "import math as _math" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from operator import index as _index" + ], + [ + "STORE_NAME", + "def _cmp(x, y):\n return 0 if x == y else 1 if x > y else -1" + ], + [ + "STORE_NAME", + "MINYEAR" + ], + [ + "STORE_NAME", + "MAXYEAR" + ], + [ + "STORE_NAME", + "_MAXORDINAL" + ], + [ + "STORE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "STORE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "STORE_NAME", + "dbm" + ], [ "LOAD_NAME", "_DAYS_IN_MONTH" @@ -7,6 +59,10 @@ "BINARY_SUBSCR", "_DAYS_IN_MONTH[1:]" ], + [ + "STORE_NAME", + "dim" + ], [ "LOAD_NAME", "_DAYS_BEFORE_MONTH" @@ -23,10 +79,46 @@ "CALL_METHOD", "_DAYS_BEFORE_MONTH.append(dbm)" ], + [ + "LOAD_NAME", + "dbm" + ], [ "LOAD_NAME", "dim" ], + [ + "STORE_NAME", + "dbm" + ], + [ + "DELETE_NAME", + "dbm" + ], + [ + "DELETE_NAME", + "dim" + ], + [ + "STORE_NAME", + "def _is_leap(year):\n \"year -> 1 if leap year, else 0.\"\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)" + ], + [ + "STORE_NAME", + "def _days_before_year(year):\n \"year -> number of days before January 1st of year.\"\n y = year - 1\n return y*365 + y//4 - y//100 + y//400" + ], + [ + "STORE_NAME", + "def _days_in_month(year, month):\n \"year, month -> number of days in that month in that year.\"\n assert 1 <= month <= 12, month\n if month == 2 and _is_leap(year):\n return 29\n return _DAYS_IN_MONTH[month]" + ], + [ + "STORE_NAME", + "def _days_before_month(year, month):\n \"year, month -> number of days in year preceding first day of month.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))" + ], + [ + "STORE_NAME", + "def _ymd2ord(year, month, day):\n \"year, month, day -> ordinal, considering 01-Jan-0001 as day 1.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n dim = _days_in_month(year, month)\n assert 1 <= day <= dim, ('day must be in 1..%d' % dim)\n return (_days_before_year(year) +\n _days_before_month(year, month) +\n day)" + ], [ "LOAD_NAME", "_days_before_year" @@ -35,6 +127,10 @@ "CALL_FUNCTION", "_days_before_year(401)" ], + [ + "STORE_NAME", + "_DI400Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -43,6 +139,10 @@ "CALL_FUNCTION", "_days_before_year(101)" ], + [ + "STORE_NAME", + "_DI100Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -51,6 +151,10 @@ "CALL_FUNCTION", "_days_before_year(5)" ], + [ + "STORE_NAME", + "_DI4Y" + ], [ "LOAD_NAME", "_DI4Y" @@ -99,6 +203,98 @@ "COMPARE_OP", "_DI100Y == 25 * _DI4Y - 1" ], + [ + "STORE_NAME", + "def _ord2ymd(n):\n \"ordinal -> (year, month, day), considering 01-Jan-0001 as day 1.\"\n\n # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years\n # repeats exactly every 400 years. The basic strategy is to find the\n # closest 400-year boundary at or before n, then work with the offset\n # from that boundary to n. Life is much clearer if we subtract 1 from\n # n first -- then the values of n at 400-year boundaries are exactly\n # those divisible by _DI400Y:\n #\n # D M Y n n-1\n # -- --- ---- ---------- ----------------\n # 31 Dec -400 -_DI400Y -_DI400Y -1\n # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary\n # ...\n # 30 Dec 000 -1 -2\n # 31 Dec 000 0 -1\n # 1 Jan 001 1 0 400-year boundary\n # 2 Jan 001 2 1\n # 3 Jan 001 3 2\n # ...\n # 31 Dec 400 _DI400Y _DI400Y -1\n # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary\n n -= 1\n n400, n = divmod(n, _DI400Y)\n year = n400 * 400 + 1 # ..., -399, 1, 401, ...\n\n # Now n is the (non-negative) offset, in days, from January 1 of year, to\n # the desired date. Now compute how many 100-year cycles precede n.\n # Note that it's possible for n100 to equal 4! In that case 4 full\n # 100-year cycles precede the desired day, which implies the desired\n # day is December 31 at the end of a 400-year cycle.\n n100, n = divmod(n, _DI100Y)\n\n # Now compute how many 4-year cycles precede it.\n n4, n = divmod(n, _DI4Y)\n\n # And now how many single years. Again n1 can be 4, and again meaning\n # that the desired day is December 31 at the end of the 4-year cycle.\n n1, n = divmod(n, 365)\n\n year += n100 * 100 + n4 * 4 + n1\n if n1 == 4 or n100 == 4:\n assert n == 0\n return year-1, 12, 31\n\n # Now the year is correct, and n is the offset from January 1. We find\n # the month via an estimate that's either exact or one too large.\n leapyear = n1 == 3 and (n4 != 24 or n100 == 3)\n assert leapyear == _is_leap(year)\n month = (n + 50) >> 5\n preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)\n if preceding > n: # estimate is too large\n month -= 1\n preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)\n n -= preceding\n assert 0 <= n < _days_in_month(year, month)\n\n # Now the year and month are correct, and n is the offset from the\n # start of that month: we're done!\n return year, month, n+1" + ], + [ + "STORE_NAME", + "_MONTHNAMES" + ], + [ + "STORE_NAME", + "_DAYNAMES" + ], + [ + "STORE_NAME", + "def _build_struct_time(y, m, d, hh, mm, ss, dstflag):\n wday = (_ymd2ord(y, m, d) + 6) % 7\n dnum = _days_before_month(y, m) + d\n return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" + ], + [ + "STORE_NAME", + "def _format_time(hh, mm, ss, us, timespec='auto'):\n specs = {\n 'hours': '{:02d}',\n 'minutes': '{:02d}:{:02d}',\n 'seconds': '{:02d}:{:02d}:{:02d}',\n 'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',\n 'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'\n }\n\n if timespec == 'auto':\n # Skip trailing microseconds when us==0.\n timespec = 'microseconds' if us else 'seconds'\n elif timespec == 'milliseconds':\n us //= 1000\n try:\n fmt = specs[timespec]\n except KeyError:\n raise ValueError('Unknown timespec value')\n else:\n return fmt.format(hh, mm, ss, us)" + ], + [ + "STORE_NAME", + "def _format_offset(off):\n s = ''\n if off is not None:\n if off.days < 0:\n sign = \"-\"\n off = -off\n else:\n sign = \"+\"\n hh, mm = divmod(off, timedelta(hours=1))\n mm, ss = divmod(mm, timedelta(minutes=1))\n s += \"%s%02d:%02d\" % (sign, hh, mm)\n if ss or ss.microseconds:\n s += \":%02d\" % ss.seconds\n\n if ss.microseconds:\n s += '.%06d' % ss.microseconds\n return s" + ], + [ + "STORE_NAME", + "def _wrap_strftime(object, format, timetuple):\n # Don't call utcoffset() or tzname() unless actually needed.\n freplace = None # the string to use for %f\n zreplace = None # the string to use for %z\n Zreplace = None # the string to use for %Z\n\n # Scan format for %z and %Z escapes, replacing as needed.\n newformat = []\n push = newformat.append\n i, n = 0, len(format)\n while i < n:\n ch = format[i]\n i += 1\n if ch == '%':\n if i < n:\n ch = format[i]\n i += 1\n if ch == 'f':\n if freplace is None:\n freplace = '%06d' % getattr(object,\n 'microsecond', 0)\n newformat.append(freplace)\n elif ch == 'z':\n if zreplace is None:\n zreplace = \"\"\n if hasattr(object, \"utcoffset\"):\n offset = object.utcoffset()\n if offset is not None:\n sign = '+'\n if offset.days < 0:\n offset = -offset\n sign = '-'\n h, rest = divmod(offset, timedelta(hours=1))\n m, rest = divmod(rest, timedelta(minutes=1))\n s = rest.seconds\n u = offset.microseconds\n if u:\n zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u)\n elif s:\n zreplace = '%c%02d%02d%02d' % (sign, h, m, s)\n else:\n zreplace = '%c%02d%02d' % (sign, h, m)\n assert '%' not in zreplace\n newformat.append(zreplace)\n elif ch == 'Z':\n if Zreplace is None:\n Zreplace = \"\"\n if hasattr(object, \"tzname\"):\n s = object.tzname()\n if s is not None:\n # strftime is going to have at this: escape %\n Zreplace = s.replace('%', '%%')\n newformat.append(Zreplace)\n else:\n push('%')\n push(ch)\n else:\n push('%')\n else:\n push(ch)\n newformat = \"\".join(newformat)\n return _time.strftime(newformat, timetuple)" + ], + [ + "STORE_NAME", + "def _is_ascii_digit(c):\n return c in \"0123456789\"" + ], + [ + "STORE_NAME", + "def _find_isoformat_datetime_separator(dtstr):\n # See the comment in _datetimemodule.c:_find_isoformat_datetime_separator\n len_dtstr = len(dtstr)\n if len_dtstr == 7:\n return 7\n\n assert len_dtstr > 7\n date_separator = \"-\"\n week_indicator = \"W\"\n\n if dtstr[4] == date_separator:\n if dtstr[5] == week_indicator:\n if len_dtstr < 8:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 8 and dtstr[8] == date_separator:\n if len_dtstr == 9:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):\n # This is as far as we need to resolve the ambiguity for\n # the moment - if we have YYYY-Www-##, the separator is\n # either a hyphen at 8 or a number at 10.\n #\n # We'll assume it's a hyphen at 8 because it's way more\n # likely that someone will use a hyphen as a separator than\n # a number, but at this point it's really best effort\n # because this is an extension of the spec anyway.\n # TODO(pganssle): Document this\n return 8\n return 10\n else:\n # YYYY-Www (8)\n return 8\n else:\n # YYYY-MM-DD (10)\n return 10\n else:\n if dtstr[4] == week_indicator:\n # YYYYWww (7) or YYYYWwwd (8)\n idx = 7\n while idx < len_dtstr:\n if not _is_ascii_digit(dtstr[idx]):\n break\n idx += 1\n\n if idx < 9:\n return idx\n\n if idx % 2 == 0:\n # If the index of the last number is even, it's YYYYWwwd\n return 7\n else:\n return 8\n else:\n # YYYYMMDD (8)\n return 8" + ], + [ + "STORE_NAME", + "def _parse_isoformat_date(dtstr):\n # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,\n # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator\n assert len(dtstr) in (7, 8, 10)\n year = int(dtstr[0:4])\n has_sep = dtstr[4] == '-'\n\n pos = 4 + has_sep\n if dtstr[pos:pos + 1] == \"W\":\n # YYYY-?Www-?D?\n pos += 1\n weekno = int(dtstr[pos:pos + 2])\n pos += 2\n\n dayno = 1\n if len(dtstr) > pos:\n if (dtstr[pos:pos + 1] == '-') != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n\n dayno = int(dtstr[pos:pos + 1])\n\n return list(_isoweek_to_gregorian(year, weekno, dayno))\n else:\n month = int(dtstr[pos:pos + 2])\n pos += 2\n if (dtstr[pos:pos + 1] == \"-\") != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n day = int(dtstr[pos:pos + 2])\n\n return [year, month, day]" + ], + [ + "STORE_NAME", + "_FRACTION_CORRECTION" + ], + [ + "STORE_NAME", + "def _parse_hh_mm_ss_ff(tstr):\n # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]\n len_str = len(tstr)\n\n time_comps = [0, 0, 0, 0]\n pos = 0\n for comp in range(0, 3):\n if (len_str - pos) < 2:\n raise ValueError(\"Incomplete time component\")\n\n time_comps[comp] = int(tstr[pos:pos+2])\n\n pos += 2\n next_char = tstr[pos:pos+1]\n\n if comp == 0:\n has_sep = next_char == ':'\n\n if not next_char or comp >= 2:\n break\n\n if has_sep and next_char != ':':\n raise ValueError(\"Invalid time separator: %c\" % next_char)\n\n pos += has_sep\n\n if pos < len_str:\n if tstr[pos] not in '.,':\n raise ValueError(\"Invalid microsecond component\")\n else:\n pos += 1\n\n len_remainder = len_str - pos\n\n if len_remainder >= 6:\n to_parse = 6\n else:\n to_parse = len_remainder\n\n time_comps[3] = int(tstr[pos:(pos+to_parse)])\n if to_parse < 6:\n time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]\n if (len_remainder > to_parse\n and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))):\n raise ValueError(\"Non-digit values in unparsed fraction\")\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _parse_isoformat_time(tstr):\n # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]\n len_str = len(tstr)\n if len_str < 2:\n raise ValueError(\"Isoformat time too short\")\n\n # This is equivalent to re.search('[+-Z]', tstr), but faster\n tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1)\n timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr\n\n time_comps = _parse_hh_mm_ss_ff(timestr)\n\n tzi = None\n if tz_pos == len_str and tstr[-1] == 'Z':\n tzi = timezone.utc\n elif tz_pos > 0:\n tzstr = tstr[tz_pos:]\n\n # Valid time zone strings are:\n # HH len: 2\n # HHMM len: 4\n # HH:MM len: 5\n # HHMMSS len: 6\n # HHMMSS.f+ len: 7+\n # HH:MM:SS len: 8\n # HH:MM:SS.f+ len: 10+\n\n if len(tzstr) in (0, 1, 3):\n raise ValueError(\"Malformed time zone string\")\n\n tz_comps = _parse_hh_mm_ss_ff(tzstr)\n\n if all(x == 0 for x in tz_comps):\n tzi = timezone.utc\n else:\n tzsign = -1 if tstr[tz_pos - 1] == '-' else 1\n\n td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])\n\n tzi = timezone(tzsign * td)\n\n time_comps.append(tzi)\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _isoweek_to_gregorian(year, week, day):\n # Year is bounded this way because 9999-12-31 is (9999, 52, 5)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError(f\"Year is out of range: {year}\")\n\n if not 0 < week < 53:\n out_of_range = True\n\n if week == 53:\n # ISO years have 53 weeks in them on years starting with a\n # Thursday and leap years starting on a Wednesday\n first_weekday = _ymd2ord(year, 1, 1) % 7\n if (first_weekday == 4 or (first_weekday == 3 and\n _is_leap(year))):\n out_of_range = False\n\n if out_of_range:\n raise ValueError(f\"Invalid week: {week}\")\n\n if not 0 < day < 8:\n raise ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")\n\n # Now compute the offset from (Y, 1, 1) in days:\n day_offset = (week - 1) * 7 + (day - 1)\n\n # Calculate the ordinal day for monday, week 1\n day_1 = _isoweek1monday(year)\n ord_day = day_1 + day_offset\n\n return _ord2ymd(ord_day)" + ], + [ + "STORE_NAME", + "def _check_tzname(name):\n if name is not None and not isinstance(name, str):\n raise TypeError(\"tzinfo.tzname() must return None or string, \"\n \"not '%s'\" % type(name))" + ], + [ + "STORE_NAME", + "def _check_utc_offset(name, offset):\n assert name in (\"utcoffset\", \"dst\")\n if offset is None:\n return\n if not isinstance(offset, timedelta):\n raise TypeError(\"tzinfo.%s() must return None \"\n \"or timedelta, not '%s'\" % (name, type(offset)))\n if not -timedelta(1) < offset < timedelta(1):\n raise ValueError(\"%s()=%s, must be strictly between \"\n \"-timedelta(hours=24) and timedelta(hours=24)\" %\n (name, offset))" + ], + [ + "STORE_NAME", + "def _check_date_fields(year, month, day):\n year = _index(year)\n month = _index(month)\n day = _index(day)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)\n if not 1 <= month <= 12:\n raise ValueError('month must be in 1..12', month)\n dim = _days_in_month(year, month)\n if not 1 <= day <= dim:\n raise ValueError('day must be in 1..%d' % dim, day)\n return year, month, day" + ], + [ + "STORE_NAME", + "def _check_time_fields(hour, minute, second, microsecond, fold):\n hour = _index(hour)\n minute = _index(minute)\n second = _index(second)\n microsecond = _index(microsecond)\n if not 0 <= hour <= 23:\n raise ValueError('hour must be in 0..23', hour)\n if not 0 <= minute <= 59:\n raise ValueError('minute must be in 0..59', minute)\n if not 0 <= second <= 59:\n raise ValueError('second must be in 0..59', second)\n if not 0 <= microsecond <= 999999:\n raise ValueError('microsecond must be in 0..999999', microsecond)\n if fold not in (0, 1):\n raise ValueError('fold must be either 0 or 1', fold)\n return hour, minute, second, microsecond, fold" + ], + [ + "STORE_NAME", + "def _check_tzinfo_arg(tz):\n if tz is not None and not isinstance(tz, tzinfo):\n raise TypeError(\"tzinfo argument must be None or of a tzinfo subclass\")" + ], + [ + "STORE_NAME", + "def _cmperror(x, y):\n raise TypeError(\"can't compare '%s' to '%s'\" % (\n type(x).__name__, type(y).__name__))" + ], + [ + "STORE_NAME", + "def _divide_and_round(a, b):\n \"\"\"divide a by b and round result to the nearest integer\n\n When the ratio is exactly half-way between two integers,\n the even integer is returned.\n \"\"\"\n # Based on the reference implementation for divmod_near\n # in Objects/longobject.c.\n q, r = divmod(a, b)\n # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.\n # The expression r / b > 0.5 is equivalent to 2 * r > b if b is\n # positive, 2 * r < b if b negative.\n r *= 2\n greater_than_half = r > b if b > 0 else r < b\n if greater_than_half or r == b and q % 2 == 1:\n q += 1\n\n return q" + ], + [ + "CALL_FUNCTION", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "timedelta" @@ -147,10 +343,22 @@ "STORE_ATTR", "timedelta.resolution" ], + [ + "CALL_FUNCTION", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "date" ], + [ + "STORE_NAME", + "_date_class" + ], [ "LOAD_NAME", "date" @@ -199,22 +407,62 @@ "STORE_ATTR", "date.resolution" ], + [ + "CALL_FUNCTION", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_NAME", "tuple" ], + [ + "CALL_FUNCTION", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_NAME", "IsoCalendarDate" ], + [ + "STORE_NAME", + "_IsoCalendarDate" + ], + [ + "DELETE_NAME", + "IsoCalendarDate" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "STORE_NAME", + "_tzinfo_class" + ], + [ + "CALL_FUNCTION", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "time" ], + [ + "STORE_NAME", + "_time_class" + ], [ "LOAD_NAME", "time" @@ -267,6 +515,14 @@ "LOAD_NAME", "date" ], + [ + "CALL_FUNCTION", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "datetime" @@ -315,10 +571,22 @@ "STORE_ATTR", "datetime.resolution" ], + [ + "STORE_NAME", + "def _isoweek1monday(year):\n # Helper to calculate the day number of the Monday starting week 1\n # XXX This could be done more efficiently\n THURSDAY = 3\n firstday = _ymd2ord(year, 1, 1)\n firstweekday = (firstday + 6) % 7 # See weekday() above\n week1monday = firstday - firstweekday\n if firstweekday > THURSDAY:\n week1monday += 7\n return week1monday" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "CALL_FUNCTION", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_NAME", "timezone" @@ -339,6 +607,10 @@ "CALL_METHOD", "timezone._create(timedelta(0))" ], + [ + "STORE_NAME", + "UTC" + ], [ "LOAD_NAME", "timezone" @@ -396,36 +668,212 @@ "timedelta(hours=23, minutes=59)" ], [ - "CALL_METHOD", - "timezone._create(timedelta(hours=23, minutes=59))" + "CALL_METHOD", + "timezone._create(timedelta(hours=23, minutes=59))" + ], + [ + "LOAD_NAME", + "timezone" + ], + [ + "STORE_ATTR", + "timezone.max" + ], + [ + "LOAD_NAME", + "datetime" + ], + [ + "LOAD_NAME", + "timezone" + ], + [ + "LOAD_ATTR", + "timezone.utc" + ], + [ + "CALL_FUNCTION_KW", + "datetime(1970, 1, 1, tzinfo=timezone.utc)" + ], + [ + "STORE_NAME", + "_EPOCH" + ], + [ + "LOAD_NAME", + "ImportError" + ], + [ + "DELETE_NAME", + "_DAYNAMES" + ], + [ + "DELETE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "DELETE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "DELETE_NAME", + "_DI100Y" + ], + [ + "DELETE_NAME", + "_DI400Y" + ], + [ + "DELETE_NAME", + "_DI4Y" + ], + [ + "DELETE_NAME", + "_EPOCH" + ], + [ + "DELETE_NAME", + "_MAXORDINAL" + ], + [ + "DELETE_NAME", + "_MONTHNAMES" + ], + [ + "DELETE_NAME", + "_build_struct_time" + ], + [ + "DELETE_NAME", + "_check_date_fields" + ], + [ + "DELETE_NAME", + "_check_time_fields" + ], + [ + "DELETE_NAME", + "_check_tzinfo_arg" + ], + [ + "DELETE_NAME", + "_check_tzname" + ], + [ + "DELETE_NAME", + "_check_utc_offset" + ], + [ + "DELETE_NAME", + "_cmp" + ], + [ + "DELETE_NAME", + "_cmperror" + ], + [ + "DELETE_NAME", + "_date_class" + ], + [ + "DELETE_NAME", + "_days_before_month" + ], + [ + "DELETE_NAME", + "_days_before_year" + ], + [ + "DELETE_NAME", + "_days_in_month" + ], + [ + "DELETE_NAME", + "_format_time" + ], + [ + "DELETE_NAME", + "_format_offset" + ], + [ + "DELETE_NAME", + "_index" + ], + [ + "DELETE_NAME", + "_is_leap" + ], + [ + "DELETE_NAME", + "_isoweek1monday" + ], + [ + "DELETE_NAME", + "_math" + ], + [ + "DELETE_NAME", + "_ord2ymd" + ], + [ + "DELETE_NAME", + "_time" + ], + [ + "DELETE_NAME", + "_time_class" + ], + [ + "DELETE_NAME", + "_tzinfo_class" + ], + [ + "DELETE_NAME", + "_wrap_strftime" + ], + [ + "DELETE_NAME", + "_ymd2ord" + ], + [ + "DELETE_NAME", + "_divide_and_round" + ], + [ + "DELETE_NAME", + "_parse_isoformat_date" + ], + [ + "DELETE_NAME", + "_parse_isoformat_time" ], [ - "LOAD_NAME", - "timezone" + "DELETE_NAME", + "_parse_hh_mm_ss_ff" ], [ - "STORE_ATTR", - "timezone.max" + "DELETE_NAME", + "_IsoCalendarDate" ], [ - "LOAD_NAME", - "datetime" + "DELETE_NAME", + "_isoweek_to_gregorian" ], [ - "LOAD_NAME", - "timezone" + "DELETE_NAME", + "_find_isoformat_datetime_separator" ], [ - "LOAD_ATTR", - "timezone.utc" + "DELETE_NAME", + "_FRACTION_CORRECTION" ], [ - "CALL_FUNCTION_KW", - "datetime(1970, 1, 1, tzinfo=timezone.utc)" + "DELETE_NAME", + "_is_ascii_digit" ], [ - "LOAD_NAME", - "ImportError" + "STORE_NAME", + "from _datetime import __doc__" ], [ "LOAD_FAST", @@ -495,6 +943,10 @@ "BINARY_SUBTRACT", "year - 1" ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_FAST", "y" @@ -543,6 +995,14 @@ "LOAD_FAST", "month" ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], [ "LOAD_FAST", "month" @@ -587,6 +1047,14 @@ "LOAD_FAST", "month" ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], [ "LOAD_GLOBAL", "_DAYS_BEFORE_MONTH" @@ -627,6 +1095,14 @@ "LOAD_FAST", "month" ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], [ "LOAD_GLOBAL", "_days_in_month" @@ -643,14 +1119,26 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" ], + [ + "COMPARE_OP", + "1 <= day <= dim" + ], [ "LOAD_FAST", "dim" ], + [ + "COMPARE_OP", + "1 <= day <= dim" + ], [ "LOAD_FAST", "dim" @@ -699,6 +1187,14 @@ "BINARY_ADD", "_days_before_year(year) +\n _days_before_month(year, month) +\n day" ], + [ + "LOAD_FAST", + "n" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -715,6 +1211,14 @@ "CALL_FUNCTION", "divmod(n, _DI400Y)" ], + [ + "STORE_FAST", + "n400" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n400" @@ -727,6 +1231,10 @@ "BINARY_ADD", "n400 * 400 + 1" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "divmod" @@ -743,6 +1251,14 @@ "CALL_FUNCTION", "divmod(n, _DI100Y)" ], + [ + "STORE_FAST", + "n100" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -759,6 +1275,14 @@ "CALL_FUNCTION", "divmod(n, _DI4Y)" ], + [ + "STORE_FAST", + "n4" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -771,6 +1295,18 @@ "CALL_FUNCTION", "divmod(n, 365)" ], + [ + "STORE_FAST", + "n1" + ], + [ + "STORE_FAST", + "n" + ], + [ + "LOAD_FAST", + "year" + ], [ "LOAD_FAST", "n100" @@ -799,6 +1335,10 @@ "BINARY_ADD", "n100 * 100 + n4 * 4 + n1" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "n1" @@ -855,6 +1395,10 @@ "COMPARE_OP", "n100 == 3" ], + [ + "STORE_FAST", + "leapyear" + ], [ "LOAD_FAST", "leapyear" @@ -887,6 +1431,10 @@ "BINARY_RSHIFT", "(n + 50) >> 5" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_DAYS_BEFORE_MONTH" @@ -915,6 +1463,10 @@ "BINARY_ADD", "_DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding" + ], [ "LOAD_FAST", "preceding" @@ -927,6 +1479,18 @@ "COMPARE_OP", "preceding > n" ], + [ + "LOAD_FAST", + "month" + ], + [ + "STORE_FAST", + "month" + ], + [ + "LOAD_FAST", + "preceding" + ], [ "LOAD_GLOBAL", "_DAYS_IN_MONTH" @@ -955,14 +1519,30 @@ "BINARY_ADD", "_DAYS_IN_MONTH[month] + (month == 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding" + ], + [ + "LOAD_FAST", + "n" + ], [ "LOAD_FAST", "preceding" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" ], + [ + "COMPARE_OP", + "0 <= n < _days_in_month(year, month)" + ], [ "LOAD_GLOBAL", "_days_in_month" @@ -979,6 +1559,10 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "COMPARE_OP", + "0 <= n < _days_in_month(year, month)" + ], [ "LOAD_FAST", "year" @@ -1023,6 +1607,10 @@ "BINARY_MODULO", "(_ymd2ord(y, m, d) + 6) % 7" ], + [ + "STORE_FAST", + "wday" + ], [ "LOAD_GLOBAL", "_days_before_month" @@ -1047,6 +1635,10 @@ "BINARY_ADD", "_days_before_month(y, m) + d" ], + [ + "STORE_FAST", + "dnum" + ], [ "LOAD_GLOBAL", "_time" @@ -1095,6 +1687,10 @@ "CALL_METHOD", "_time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" ], + [ + "STORE_FAST", + "specs" + ], [ "LOAD_FAST", "timespec" @@ -1107,6 +1703,10 @@ "LOAD_FAST", "us" ], + [ + "STORE_FAST", + "timespec" + ], [ "LOAD_FAST", "timespec" @@ -1115,6 +1715,14 @@ "COMPARE_OP", "timespec == 'milliseconds'" ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "specs" @@ -1127,6 +1735,10 @@ "BINARY_SUBSCR", "specs[timespec]" ], + [ + "STORE_FAST", + "fmt" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1167,6 +1779,10 @@ "CALL_METHOD", "fmt.format(hh, mm, ss, us)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "off" @@ -1187,6 +1803,10 @@ "COMPARE_OP", "off.days < 0" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "off" @@ -1195,6 +1815,14 @@ "UNARY_NEGATIVE", "-off" ], + [ + "STORE_FAST", + "off" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1215,6 +1843,14 @@ "CALL_FUNCTION", "divmod(off, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_GLOBAL", "divmod" @@ -1235,6 +1871,18 @@ "CALL_FUNCTION", "divmod(mm, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "sign" @@ -1251,6 +1899,10 @@ "BINARY_MODULO", "\"%s%02d:%02d\" % (sign, hh, mm)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1263,6 +1915,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1275,6 +1931,10 @@ "BINARY_MODULO", "\":%02d\" % ss.seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1283,6 +1943,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1295,10 +1959,30 @@ "BINARY_MODULO", "'.%06d' % ss.microseconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" ], + [ + "STORE_FAST", + "freplace" + ], + [ + "STORE_FAST", + "zreplace" + ], + [ + "STORE_FAST", + "Zreplace" + ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_FAST", "newformat" @@ -1307,6 +1991,10 @@ "LOAD_ATTR", "newformat.append" ], + [ + "STORE_FAST", + "push" + ], [ "LOAD_GLOBAL", "len" @@ -1319,6 +2007,14 @@ "CALL_FUNCTION", "len(format)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "i" @@ -1343,6 +2039,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "ch" @@ -1375,6 +2083,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "ch" @@ -1407,6 +2127,10 @@ "BINARY_MODULO", "'%06d' % getattr(object,\n 'microsecond', 0)" ], + [ + "STORE_FAST", + "freplace" + ], [ "LOAD_FAST", "newformat" @@ -1439,6 +2163,10 @@ "IS_OP", "zreplace is None" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1463,6 +2191,10 @@ "CALL_METHOD", "object.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" @@ -1471,6 +2203,10 @@ "IS_OP", "offset is not None" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "offset" @@ -1491,6 +2227,14 @@ "UNARY_NEGATIVE", "-offset" ], + [ + "STORE_FAST", + "offset" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1511,6 +2255,14 @@ "CALL_FUNCTION", "divmod(offset, timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -1531,6 +2283,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -1539,6 +2299,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "offset" @@ -1547,6 +2311,10 @@ "LOAD_ATTR", "offset.microseconds" ], + [ + "STORE_FAST", + "u" + ], [ "LOAD_FAST", "u" @@ -1575,6 +2343,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d.%06d' % (sign, h, m, s, u)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "s" @@ -1599,6 +2371,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d' % (sign, h, m, s)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "sign" @@ -1615,6 +2391,10 @@ "BINARY_MODULO", "'%c%02d%02d' % (sign, h, m)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "zreplace" @@ -1655,6 +2435,10 @@ "IS_OP", "Zreplace is None" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1679,6 +2463,10 @@ "CALL_METHOD", "object.tzname()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -1699,6 +2487,10 @@ "CALL_METHOD", "s.replace('%', '%%')" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_FAST", "newformat" @@ -1779,6 +2571,10 @@ "CALL_METHOD", "\"\".join(newformat)" ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_GLOBAL", "_time" @@ -1819,6 +2615,10 @@ "CALL_FUNCTION", "len(dtstr)" ], + [ + "STORE_FAST", + "len_dtstr" + ], [ "LOAD_FAST", "len_dtstr" @@ -1835,6 +2635,14 @@ "COMPARE_OP", "len_dtstr > 7" ], + [ + "STORE_FAST", + "date_separator" + ], + [ + "STORE_FAST", + "week_indicator" + ], [ "LOAD_FAST", "dtstr" @@ -1963,6 +2771,10 @@ "COMPARE_OP", "dtstr[4] == week_indicator" ], + [ + "STORE_FAST", + "idx" + ], [ "LOAD_FAST", "idx" @@ -1999,6 +2811,14 @@ "LOAD_FAST", "idx" ], + [ + "STORE_FAST", + "idx" + ], + [ + "LOAD_FAST", + "idx" + ], [ "LOAD_FAST", "len_dtstr" @@ -2063,6 +2883,10 @@ "CALL_FUNCTION", "int(dtstr[0:4])" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "dtstr" @@ -2075,6 +2899,10 @@ "COMPARE_OP", "dtstr[4] == '-'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "has_sep" @@ -2083,6 +2911,10 @@ "BINARY_ADD", "4 + has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "dtstr" @@ -2107,6 +2939,14 @@ "COMPARE_OP", "dtstr[pos:pos + 1] == \"W\"" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2135,6 +2975,22 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "weekno" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "len" @@ -2195,10 +3051,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2227,6 +3091,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 1])" ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "list" @@ -2283,6 +3151,18 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "month" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "dtstr" @@ -2323,10 +3203,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2355,6 +3243,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "year" @@ -2379,6 +3271,18 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "range" @@ -2387,6 +3291,10 @@ "CALL_FUNCTION", "range(0, 3)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_FAST", "len_str" @@ -2451,6 +3359,14 @@ "STORE_SUBSCR", "time_comps[comp]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "tstr" @@ -2471,6 +3387,10 @@ "BINARY_SUBSCR", "tstr[pos:pos+1]" ], + [ + "STORE_FAST", + "next_char" + ], [ "LOAD_FAST", "comp" @@ -2487,6 +3407,10 @@ "COMPARE_OP", "next_char == ':'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "next_char" @@ -2527,10 +3451,18 @@ "CALL_FUNCTION", "ValueError(\"Invalid time separator: %c\" % next_char)" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -2567,6 +3499,14 @@ "CALL_FUNCTION", "ValueError(\"Invalid microsecond component\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "len_str" @@ -2579,6 +3519,10 @@ "BINARY_SUBTRACT", "len_str - pos" ], + [ + "STORE_FAST", + "len_remainder" + ], [ "LOAD_FAST", "len_remainder" @@ -2587,10 +3531,18 @@ "COMPARE_OP", "len_remainder >= 6" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_FAST", "len_remainder" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_GLOBAL", "int" @@ -2643,6 +3595,10 @@ "LOAD_FAST", "time_comps" ], + [ + "BINARY_SUBSCR", + "time_comps[3]" + ], [ "LOAD_GLOBAL", "_FRACTION_CORRECTION" @@ -2739,6 +3695,10 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], [ "LOAD_FAST", "len_str" @@ -2803,6 +3763,10 @@ "BINARY_ADD", "tstr.find('Z') + 1" ], + [ + "STORE_FAST", + "tz_pos" + ], [ "LOAD_FAST", "tz_pos" @@ -2831,6 +3795,10 @@ "LOAD_FAST", "tstr" ], + [ + "STORE_FAST", + "timestr" + ], [ "LOAD_GLOBAL", "_parse_hh_mm_ss_ff" @@ -2843,6 +3811,14 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(timestr)" ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2875,6 +3851,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2895,6 +3875,10 @@ "BINARY_SUBSCR", "tstr[tz_pos:]" ], + [ + "STORE_FAST", + "tzstr" + ], [ "LOAD_GLOBAL", "len" @@ -2931,6 +3915,10 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(tzstr)" ], + [ + "STORE_FAST", + "tz_comps" + ], [ "LOAD_GLOBAL", "all" @@ -2939,6 +3927,10 @@ "LOAD_FAST", "tz_comps" ], + [ + "CALL_FUNCTION", + "(x == 0 for x in tz_comps)" + ], [ "CALL_FUNCTION", "all(x == 0 for x in tz_comps)" @@ -2951,6 +3943,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tstr" @@ -2971,6 +3967,10 @@ "COMPARE_OP", "tstr[tz_pos - 1] == '-'" ], + [ + "STORE_FAST", + "tzsign" + ], [ "LOAD_GLOBAL", "timedelta" @@ -3011,6 +4011,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])" ], + [ + "STORE_FAST", + "td" + ], [ "LOAD_GLOBAL", "timezone" @@ -3031,6 +4035,10 @@ "CALL_FUNCTION", "timezone(tzsign * td)" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "time_comps" @@ -3051,6 +4059,14 @@ "LOAD_FAST", "time_comps" ], + [ + "LOAD_FAST", + "(x == 0 for x in tz_comps)" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -3067,10 +4083,18 @@ "LOAD_FAST", "year" ], + [ + "COMPARE_OP", + "MINYEAR <= year <= MAXYEAR" + ], [ "LOAD_GLOBAL", "MAXYEAR" ], + [ + "COMPARE_OP", + "MINYEAR <= year <= MAXYEAR" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3079,6 +4103,10 @@ "LOAD_FAST", "year" ], + [ + "BUILD_STRING", + "f\"Year is out of range: {year}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Year is out of range: {year}\")" @@ -3087,6 +4115,18 @@ "LOAD_FAST", "week" ], + [ + "COMPARE_OP", + "0 < week < 53" + ], + [ + "COMPARE_OP", + "0 < week < 53" + ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "week" @@ -3111,6 +4151,10 @@ "BINARY_MODULO", "_ymd2ord(year, 1, 1) % 7" ], + [ + "STORE_FAST", + "first_weekday" + ], [ "LOAD_FAST", "first_weekday" @@ -3139,6 +4183,10 @@ "CALL_FUNCTION", "_is_leap(year)" ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "out_of_range" @@ -3151,6 +4199,10 @@ "LOAD_FAST", "week" ], + [ + "BUILD_STRING", + "f\"Invalid week: {week}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid week: {week}\")" @@ -3159,6 +4211,14 @@ "LOAD_FAST", "day" ], + [ + "COMPARE_OP", + "0 < day < 8" + ], + [ + "COMPARE_OP", + "0 < day < 8" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3167,6 +4227,10 @@ "LOAD_FAST", "day" ], + [ + "BUILD_STRING", + "f\"Invalid weekday: {day} (range is [1, 7])\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")" @@ -3195,6 +4259,10 @@ "BINARY_ADD", "(week - 1) * 7 + (day - 1)" ], + [ + "STORE_FAST", + "day_offset" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -3207,6 +4275,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "day_1" + ], [ "LOAD_FAST", "day_1" @@ -3219,6 +4291,10 @@ "BINARY_ADD", "day_1 + day_offset" ], + [ + "STORE_FAST", + "ord_day" + ], [ "LOAD_GLOBAL", "_ord2ymd" @@ -3355,6 +4431,10 @@ "LOAD_FAST", "offset" ], + [ + "COMPARE_OP", + "-timedelta(1) < offset < timedelta(1)" + ], [ "LOAD_GLOBAL", "timedelta" @@ -3363,6 +4443,10 @@ "CALL_FUNCTION", "timedelta(1)" ], + [ + "COMPARE_OP", + "-timedelta(1) < offset < timedelta(1)" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3395,6 +4479,10 @@ "CALL_FUNCTION", "_index(year)" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_index" @@ -3407,6 +4495,10 @@ "CALL_FUNCTION", "_index(month)" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_index" @@ -3419,6 +4511,10 @@ "CALL_FUNCTION", "_index(day)" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "MINYEAR" @@ -3427,10 +4523,18 @@ "LOAD_FAST", "year" ], + [ + "COMPARE_OP", + "MINYEAR <= year <= MAXYEAR" + ], [ "LOAD_GLOBAL", "MAXYEAR" ], + [ + "COMPARE_OP", + "MINYEAR <= year <= MAXYEAR" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3459,6 +4563,14 @@ "LOAD_FAST", "month" ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], + [ + "COMPARE_OP", + "1 <= month <= 12" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3487,14 +4599,26 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" ], + [ + "COMPARE_OP", + "1 <= day <= dim" + ], [ "LOAD_FAST", "dim" ], + [ + "COMPARE_OP", + "1 <= day <= dim" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3539,6 +4663,10 @@ "CALL_FUNCTION", "_index(hour)" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "_index" @@ -3551,6 +4679,10 @@ "CALL_FUNCTION", "_index(minute)" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_GLOBAL", "_index" @@ -3563,6 +4695,10 @@ "CALL_FUNCTION", "_index(second)" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_GLOBAL", "_index" @@ -3575,10 +4711,22 @@ "CALL_FUNCTION", "_index(microsecond)" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "hour" ], + [ + "COMPARE_OP", + "0 <= hour <= 23" + ], + [ + "COMPARE_OP", + "0 <= hour <= 23" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3595,6 +4743,14 @@ "LOAD_FAST", "minute" ], + [ + "COMPARE_OP", + "0 <= minute <= 59" + ], + [ + "COMPARE_OP", + "0 <= minute <= 59" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3611,6 +4767,14 @@ "LOAD_FAST", "second" ], + [ + "COMPARE_OP", + "0 <= second <= 59" + ], + [ + "COMPARE_OP", + "0 <= second <= 59" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3627,6 +4791,14 @@ "LOAD_FAST", "microsecond" ], + [ + "COMPARE_OP", + "0 <= microsecond <= 999999" + ], + [ + "COMPARE_OP", + "0 <= microsecond <= 999999" + ], [ "LOAD_GLOBAL", "ValueError" @@ -3771,6 +4943,22 @@ "CALL_FUNCTION", "divmod(a, b)" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], + [ + "LOAD_FAST", + "r" + ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_FAST", "b" @@ -3803,6 +4991,10 @@ "COMPARE_OP", "r < b" ], + [ + "STORE_FAST", + "greater_than_half" + ], [ "LOAD_FAST", "greater_than_half" @@ -3835,6 +5027,50 @@ "LOAD_FAST", "q" ], + [ + "STORE_FAST", + "q" + ], + [ + "LOAD_FAST", + "q" + ], + [ + "LOAD_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))" + ], + [ + "STORE_NAME", + " def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s" + ], + [ + "STORE_NAME", + " def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6" + ], [ "LOAD_NAME", "property" @@ -3843,6 +5079,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days" + ], [ "LOAD_NAME", "property" @@ -3851,6 +5091,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds" + ], [ "LOAD_NAME", "property" @@ -3859,14 +5103,130 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)" + ], + [ + "STORE_NAME", + " def __pos__(self):\n return self" + ], + [ + "STORE_NAME", + " def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self" + ], + [ + "STORE_NAME", + " def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented" + ], [ "LOAD_NAME", "__mul__" ], + [ + "STORE_NAME", + "__rmul__" + ], + [ + "STORE_NAME", + " def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)" + ], + [ + "STORE_NAME", + " def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)" + ], + [ + "STORE_NAME", + " def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))" + ], + [ + "STORE_NAME", + " def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())" + ], + [ + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" + ], + [ + "STORE_NAME", + " def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)" + ], + [ + "STORE_NAME", + " def _getstate(self):\n return (self._days, self._seconds, self._microseconds)" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "s" + ], + [ + "STORE_FAST", + "us" + ], + [ + "LOAD_FAST", + "days" + ], [ "LOAD_FAST", "weeks" @@ -3875,6 +5235,14 @@ "BINARY_MULTIPLY", "weeks*7" ], + [ + "STORE_FAST", + "days" + ], + [ + "LOAD_FAST", + "seconds" + ], [ "LOAD_FAST", "minutes" @@ -3895,6 +5263,14 @@ "BINARY_ADD", "minutes*60 + hours*3600" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "microseconds" + ], [ "LOAD_FAST", "milliseconds" @@ -3903,6 +5279,10 @@ "BINARY_MULTIPLY", "milliseconds*1000" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3935,6 +5315,14 @@ "CALL_METHOD", "_math.modf(days)" ], + [ + "STORE_FAST", + "dayfrac" + ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_GLOBAL", "_math" @@ -3955,6 +5343,14 @@ "CALL_METHOD", "_math.modf(dayfrac * (24.*3600.))" ], + [ + "STORE_FAST", + "daysecondsfrac" + ], + [ + "STORE_FAST", + "daysecondswhole" + ], [ "LOAD_FAST", "daysecondswhole" @@ -3987,6 +5383,10 @@ "CALL_FUNCTION", "int(daysecondswhole)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "days" @@ -4019,10 +5419,22 @@ "CALL_FUNCTION", "int(days)" ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "daysecondsfrac" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4119,6 +5531,14 @@ "CALL_METHOD", "_math.modf(seconds)" ], + [ + "STORE_FAST", + "secondsfrac" + ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "seconds" @@ -4151,10 +5571,22 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "secondsfrac" + ], [ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac" + ], [ "LOAD_GLOBAL", "abs" @@ -4175,6 +5607,10 @@ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4229,15 +5665,35 @@ ], [ "LOAD_FAST", - "seconds" + "seconds" + ], + [ + "CALL_FUNCTION", + "divmod(seconds, 24*3600)" + ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], + [ + "LOAD_FAST", + "days" ], [ - "CALL_FUNCTION", - "divmod(seconds, 24*3600)" + "STORE_FAST", + "d" ], [ "LOAD_FAST", - "days" + "s" ], [ "LOAD_GLOBAL", @@ -4251,6 +5707,10 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4291,6 +5751,10 @@ "BINARY_MULTIPLY", "secondsfrac * 1e6" ], + [ + "STORE_FAST", + "usdouble" + ], [ "LOAD_GLOBAL", "abs" @@ -4343,6 +5807,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4355,6 +5823,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4367,14 +5843,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "int" @@ -4387,6 +5887,10 @@ "CALL_FUNCTION", "int(microseconds)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4399,6 +5903,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4411,14 +5923,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "round" @@ -4439,6 +5975,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4515,10 +6055,26 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "us" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "divmod" @@ -4531,10 +6087,26 @@ "CALL_FUNCTION", "divmod(s, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "s" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4571,6 +6143,14 @@ "LOAD_FAST", "s" ], + [ + "COMPARE_OP", + "0 <= s < 24*3600" + ], + [ + "COMPARE_OP", + "0 <= s < 24*3600" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4591,6 +6171,14 @@ "LOAD_FAST", "us" ], + [ + "COMPARE_OP", + "0 <= us < 1000000" + ], + [ + "COMPARE_OP", + "0 <= us < 1000000" + ], [ "LOAD_GLOBAL", "abs" @@ -4639,6 +6227,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "d" @@ -4687,6 +6279,10 @@ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -4855,6 +6451,14 @@ "CALL_FUNCTION", "divmod(self._seconds, 60)" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "divmod" @@ -4867,6 +6471,14 @@ "CALL_FUNCTION", "divmod(mm, 60)" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_FAST", "hh" @@ -4883,6 +6495,10 @@ "BINARY_MODULO", "\"%d:%02d:%02d\" % (hh, mm, ss)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4891,6 +6507,10 @@ "LOAD_ATTR", "self._days" ], + [ + "STORE_FAST", + " def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"" + ], [ "LOAD_FAST", "plural" @@ -4919,6 +6539,10 @@ "BINARY_ADD", "(\"%d day%s, \" % plural(self._days)) + s" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4947,6 +6571,10 @@ "BINARY_ADD", "s + \".%06d\" % self._microseconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -5423,6 +7051,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_FAST", "other" @@ -5435,6 +7067,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5547,6 +7187,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5659,6 +7303,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5763,6 +7411,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5839,6 +7495,10 @@ "BINARY_MODULO", "self._to_microseconds() % other._to_microseconds()" ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5903,6 +7563,14 @@ "CALL_FUNCTION", "divmod(self._to_microseconds(),\n other._to_microseconds())" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_FAST", "q" @@ -6299,6 +7967,30 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self" + ], [ "LOAD_NAME", "classmethod" @@ -6307,6 +7999,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6315,6 +8011,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -6323,6 +8023,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6331,6 +8035,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')" + ], [ "LOAD_NAME", "classmethod" @@ -6339,10 +8047,38 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)" + ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "property" @@ -6351,6 +8087,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year" + ], [ "LOAD_NAME", "property" @@ -6359,17 +8099,101 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month" + ], [ "LOAD_NAME", "property" ], [ - "CALL_FUNCTION", - "property" + "CALL_FUNCTION", + "property" + ], + [ + "STORE_NAME", + " @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)" + ], + [ + "STORE_NAME", + " def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented" + ], + [ + "LOAD_NAME", + "__add__" + ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7" + ], + [ + "STORE_NAME", + " def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7" + ], + [ + "STORE_NAME", + " def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)" + ], + [ + "STORE_NAME", + " def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day])," + ], + [ + "STORE_NAME", + " def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo" ], [ - "LOAD_NAME", - "__add__" + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" ], [ "LOAD_FAST", @@ -6431,6 +8255,14 @@ "CALL_FUNCTION", "ord(year[2:3])" ], + [ + "COMPARE_OP", + "1 <= ord(year[2:3]) <= 12" + ], + [ + "COMPARE_OP", + "1 <= ord(year[2:3]) <= 12" + ], [ "LOAD_GLOBAL", "isinstance" @@ -6459,6 +8291,10 @@ "CALL_METHOD", "year.encode('latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -6487,6 +8323,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -6535,6 +8375,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "object" @@ -6551,6 +8403,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -6615,6 +8471,42 @@ "CALL_METHOD", "_time.localtime(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "cls" @@ -6647,6 +8539,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -6675,6 +8571,18 @@ "CALL_FUNCTION", "_ord2ymd(n)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "cls" @@ -6743,6 +8651,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6779,6 +8691,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6879,6 +8795,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -7175,6 +9095,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -7191,6 +9115,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -7207,6 +9135,10 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "type" @@ -7475,6 +9407,18 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "other" @@ -7499,6 +9443,18 @@ "LOAD_ATTR", "other._day" ], + [ + "STORE_FAST", + "y2" + ], + [ + "STORE_FAST", + "m2" + ], + [ + "STORE_FAST", + "d2" + ], [ "LOAD_GLOBAL", "_cmp" @@ -7619,14 +9575,26 @@ "BINARY_ADD", "self.toordinal() + other.days" ], + [ + "STORE_FAST", + "o" + ], [ "LOAD_FAST", "o" ], + [ + "COMPARE_OP", + "0 < o <= _MAXORDINAL" + ], [ "LOAD_GLOBAL", "_MAXORDINAL" ], + [ + "COMPARE_OP", + "0 < o <= _MAXORDINAL" + ], [ "LOAD_GLOBAL", "OverflowError" @@ -7743,6 +9711,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -7755,6 +9727,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -7823,6 +9799,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7835,6 +9815,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -7867,6 +9851,10 @@ "CALL_FUNCTION", "_ymd2ord(self._year, self._month, self._day)" ], + [ + "STORE_FAST", + "today" + ], [ "LOAD_GLOBAL", "divmod" @@ -7887,6 +9875,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7895,6 +9891,14 @@ "COMPARE_OP", "week < 0" ], + [ + "LOAD_FAST", + "year" + ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7907,6 +9911,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "divmod" @@ -7927,6 +9935,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7959,6 +9975,18 @@ "COMPARE_OP", "today >= _isoweek1monday(year+1)" ], + [ + "LOAD_FAST", + "year" + ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "week" + ], [ "LOAD_GLOBAL", "_IsoCalendarDate" @@ -8003,6 +10031,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "bytes" @@ -8039,6 +10075,14 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_FAST", "self" @@ -8099,6 +10143,46 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "\"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_GLOBAL", "NotImplementedError" @@ -8183,6 +10267,10 @@ "CALL_METHOD", "dt.utcoffset()" ], + [ + "STORE_FAST", + "dtoff" + ], [ "LOAD_FAST", "dtoff" @@ -8211,6 +10299,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8239,14 +10331,26 @@ "BINARY_SUBTRACT", "dtoff - dtdst" ], + [ + "STORE_FAST", + "delta" + ], [ "LOAD_FAST", "delta" ], + [ + "LOAD_FAST", + "dt" + ], [ "LOAD_FAST", "delta" ], + [ + "STORE_FAST", + "dt" + ], [ "LOAD_FAST", "dt" @@ -8259,6 +10363,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8299,6 +10407,10 @@ "CALL_FUNCTION", "getattr(self, \"__getinitargs__\", None)" ], + [ + "STORE_FAST", + "getinitargs" + ], [ "LOAD_FAST", "getinitargs" @@ -8311,6 +10423,14 @@ "CALL_FUNCTION", "getinitargs()" ], + [ + "STORE_FAST", + "args" + ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -8335,6 +10455,22 @@ "CALL_METHOD", "self.__getstate__()" ], + [ + "LOAD_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + " def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))" + ], [ "LOAD_NAME", "property" @@ -8343,6 +10479,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n return self[0]" + ], [ "LOAD_NAME", "property" @@ -8351,6 +10491,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def week(self):\n return self[1]" + ], [ "LOAD_NAME", "property" @@ -8359,6 +10503,18 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def weekday(self):\n return self[2]" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_GLOBAL", "super" @@ -8460,12 +10616,40 @@ "self[1]" ], [ - "LOAD_FAST", - "self" + "LOAD_FAST", + "self" + ], + [ + "BINARY_SUBSCR", + "self[2]" + ], + [ + "BUILD_STRING", + "f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})'" + ], + [ + "LOAD_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" ], [ - "BINARY_SUBSCR", - "self[2]" + "STORE_NAME", + " def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" ], [ "LOAD_NAME", @@ -8475,6 +10659,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -8483,6 +10671,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -8491,6 +10683,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -8499,6 +10695,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -8507,6 +10707,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -8515,10 +10719,58 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s" + ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "classmethod" @@ -8527,6 +10779,50 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -8615,6 +10911,10 @@ "CALL_METHOD", "hour.encode('latin1')" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -8643,6 +10943,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -8703,6 +11007,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -8731,6 +11055,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "hour" @@ -9087,6 +11415,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -9095,6 +11427,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -9107,6 +11451,10 @@ "IS_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -9119,6 +11467,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -9131,6 +11483,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -9143,6 +11499,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -9291,6 +11651,10 @@ "BINARY_SUBTRACT", "self._hour * 60 + self._minute - myoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "myhhmm" + ], [ "LOAD_FAST", "other" @@ -9335,6 +11699,10 @@ "BINARY_SUBTRACT", "other._hour * 60 + other._minute - otoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "othhmm" + ], [ "LOAD_GLOBAL", "_cmp" @@ -9415,10 +11783,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -9431,6 +11807,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -9523,6 +11903,14 @@ "CALL_FUNCTION", "divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "m" @@ -9539,6 +11927,10 @@ "BINARY_MODULO", "m % timedelta(minutes=1)" ], + [ + "LOAD_FAST", + "m" + ], [ "LOAD_GLOBAL", "timedelta" @@ -9547,10 +11939,22 @@ "CALL_FUNCTION_KW", "timedelta(minutes=1)" ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "h" ], + [ + "COMPARE_OP", + "0 <= h < 24" + ], + [ + "COMPARE_OP", + "0 <= h < 24" + ], [ "LOAD_GLOBAL", "hash" @@ -9667,6 +12071,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -9711,6 +12119,10 @@ "BINARY_MODULO", "\", %d, %d\" % (self._second, self._microsecond)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9735,6 +12147,14 @@ "BINARY_MODULO", "\", %d\" % self._second" ], + [ + "STORE_FAST", + "s" + ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9783,6 +12203,10 @@ "BINARY_MODULO", "\"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9835,6 +12259,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9867,6 +12295,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -9915,6 +12347,10 @@ "CALL_FUNCTION", "_format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9927,14 +12363,26 @@ "CALL_METHOD", "self._tzstr()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -9975,6 +12423,10 @@ "CALL_METHOD", "time_string.removeprefix('T')" ], + [ + "STORE_FAST", + "time_string" + ], [ "LOAD_FAST", "cls" @@ -10007,6 +12459,10 @@ "LOAD_FAST", "time_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {time_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {time_string!r}')" @@ -10035,6 +12491,10 @@ "LOAD_ATTR", "self._second" ], + [ + "STORE_FAST", + "timetuple" + ], [ "LOAD_GLOBAL", "_wrap_strftime" @@ -10171,6 +12631,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10215,6 +12679,10 @@ "CALL_METHOD", "self._tzinfo.tzname(None)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -10259,6 +12727,10 @@ "CALL_METHOD", "self._tzinfo.dst(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10291,6 +12763,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -10307,6 +12783,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -10323,6 +12803,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -10339,6 +12823,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -10355,6 +12843,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -10371,6 +12863,10 @@ "LOAD_ATTR", "self._fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -10427,6 +12923,14 @@ "CALL_FUNCTION", "divmod(self._microsecond, 256)" ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_GLOBAL", "divmod" @@ -10439,6 +12943,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -10447,6 +12959,10 @@ "LOAD_ATTR", "self._hour" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10463,6 +12979,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "h" + ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_GLOBAL", "bytes" @@ -10503,6 +13027,10 @@ "CALL_FUNCTION", "bytes([h, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -10567,6 +13095,10 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10583,6 +13115,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "h" @@ -10719,6 +13263,22 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "LOAD_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"" + ], [ "LOAD_NAME", "date" @@ -10739,6 +13299,14 @@ "BINARY_ADD", "date.__slots__ + time.__slots__" ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" + ], [ "LOAD_NAME", "property" @@ -10747,6 +13315,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -10755,6 +13327,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -10763,6 +13339,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -10771,6 +13351,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -10779,6 +13363,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -10787,6 +13375,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], [ "LOAD_NAME", "classmethod" @@ -10795,6 +13387,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -10803,6 +13399,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10811,6 +13411,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)" + ], [ "LOAD_NAME", "classmethod" @@ -10819,6 +13423,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10827,6 +13435,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -10835,6 +13447,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)" + ], [ "LOAD_NAME", "classmethod" @@ -10843,6 +13459,66 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)" + ], + [ + "STORE_NAME", + " def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)" + ], + [ + "STORE_NAME", + " def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()" + ], + [ + "STORE_NAME", + " def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)" + ], + [ + "STORE_NAME", + " def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)" + ], + [ + "STORE_NAME", + " def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)" + ], + [ + "STORE_NAME", + " def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)" + ], + [ + "STORE_NAME", + " def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')" + ], [ "LOAD_NAME", "classmethod" @@ -10851,10 +13527,82 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff" + ], + [ + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -10911,6 +13659,14 @@ "BINARY_AND", "ord(year[2:3])&0x7F" ], + [ + "COMPARE_OP", + "1 <= ord(year[2:3])&0x7F <= 12" + ], + [ + "COMPARE_OP", + "1 <= ord(year[2:3])&0x7F <= 12" + ], [ "LOAD_GLOBAL", "isinstance" @@ -10939,6 +13695,10 @@ "CALL_FUNCTION", "bytes(year, 'latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -10967,6 +13727,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -11019,6 +13783,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "_check_time_fields" @@ -11047,6 +13823,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -11075,6 +13871,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -11259,6 +14059,14 @@ "CALL_METHOD", "_math.modf(t)" ], + [ + "STORE_FAST", + "frac" + ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_GLOBAL", "round" @@ -11275,6 +14083,10 @@ "CALL_FUNCTION", "round(frac * 1e6)" ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "us" @@ -11283,6 +14095,22 @@ "COMPARE_OP", "us >= 1000000" ], + [ + "LOAD_FAST", + "t" + ], + [ + "STORE_FAST", + "t" + ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "us" @@ -11291,6 +14119,22 @@ "COMPARE_OP", "us < 0" ], + [ + "LOAD_FAST", + "t" + ], + [ + "STORE_FAST", + "t" + ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "utc" @@ -11311,6 +14155,10 @@ "LOAD_ATTR", "_time.localtime" ], + [ + "STORE_FAST", + "converter" + ], [ "LOAD_FAST", "converter" @@ -11323,6 +14171,42 @@ "CALL_FUNCTION", "converter(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "min" @@ -11335,6 +14219,10 @@ "CALL_FUNCTION", "min(ss, 59)" ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11375,6 +14263,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "tz" @@ -11387,6 +14279,10 @@ "LOAD_FAST", "utc" ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "t" @@ -11443,6 +14339,30 @@ "BINARY_SUBSCR", "converter(t - max_fold_seconds)[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11483,6 +14403,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe1" + ], [ "LOAD_FAST", "result" @@ -11511,6 +14435,10 @@ "BINARY_SUBTRACT", "result - probe1 - timedelta(0, max_fold_seconds)" ], + [ + "STORE_FAST", + "trans" + ], [ "LOAD_FAST", "trans" @@ -11540,24 +14468,48 @@ "timedelta" ], [ - "CALL_FUNCTION", - "timedelta(0, 1)" + "CALL_FUNCTION", + "timedelta(0, 1)" + ], + [ + "BINARY_FLOOR_DIVIDE", + "trans // timedelta(0, 1)" + ], + [ + "BINARY_ADD", + "t + trans // timedelta(0, 1)" + ], + [ + "CALL_FUNCTION", + "converter(t + trans // timedelta(0, 1))" + ], + [ + "BINARY_SUBSCR", + "converter(t + trans // timedelta(0, 1))[:6]" + ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" ], [ - "BINARY_FLOOR_DIVIDE", - "trans // timedelta(0, 1)" + "STORE_FAST", + "d" ], [ - "BINARY_ADD", - "t + trans // timedelta(0, 1)" + "STORE_FAST", + "hh" ], [ - "CALL_FUNCTION", - "converter(t + trans // timedelta(0, 1))" + "STORE_FAST", + "mm" ], [ - "BINARY_SUBSCR", - "converter(t + trans // timedelta(0, 1))[:6]" + "STORE_FAST", + "ss" ], [ "LOAD_FAST", @@ -11599,6 +14551,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe2" + ], [ "LOAD_FAST", "probe2" @@ -11647,6 +14603,10 @@ "CALL_METHOD", "tz.fromutc(result)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -11719,6 +14679,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11751,6 +14715,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11831,6 +14799,10 @@ "LOAD_ATTR", "time.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "cls" @@ -11955,6 +14927,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -11971,6 +14947,10 @@ "CALL_FUNCTION", "_find_isoformat_datetime_separator(date_string)" ], + [ + "STORE_FAST", + "separator_location" + ], [ "LOAD_FAST", "date_string" @@ -11983,6 +14963,10 @@ "BINARY_SUBSCR", "date_string[0:separator_location]" ], + [ + "STORE_FAST", + "dstr" + ], [ "LOAD_FAST", "date_string" @@ -11999,6 +14983,10 @@ "BINARY_SUBSCR", "date_string[(separator_location+1):]" ], + [ + "STORE_FAST", + "tstr" + ], [ "LOAD_GLOBAL", "_parse_isoformat_date" @@ -12011,6 +14999,10 @@ "CALL_FUNCTION", "_parse_isoformat_date(dstr)" ], + [ + "STORE_FAST", + "date_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -12023,6 +15015,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" @@ -12043,6 +15039,10 @@ "CALL_FUNCTION", "_parse_isoformat_time(tstr)" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -12055,10 +15055,18 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_FAST", "cls" @@ -12091,6 +15099,10 @@ "CALL_METHOD", "self.dst()" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" @@ -12099,10 +15111,22 @@ "IS_OP", "dst is None" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" ], + [ + "STORE_FAST", + "dst" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12171,6 +15195,14 @@ "CALL_FUNCTION", "datetime(1970, 1, 1)" ], + [ + "STORE_DEREF", + "epoch" + ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "self" @@ -12195,6 +15227,14 @@ "BINARY_FLOOR_DIVIDE", "(self - epoch) // timedelta(0, 1)" ], + [ + "STORE_FAST", + "t" + ], + [ + "STORE_FAST", + " def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)" + ], [ "LOAD_FAST", "local" @@ -12215,6 +15255,10 @@ "BINARY_SUBTRACT", "local(t) - t" ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_FAST", "t" @@ -12227,6 +15271,10 @@ "BINARY_SUBTRACT", "t - a" ], + [ + "STORE_FAST", + "u1" + ], [ "LOAD_FAST", "local" @@ -12239,6 +15287,10 @@ "CALL_FUNCTION", "local(u1)" ], + [ + "STORE_FAST", + "t1" + ], [ "LOAD_FAST", "t1" @@ -12283,6 +15335,10 @@ "BINARY_ADD", "u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12303,6 +15359,10 @@ "BINARY_SUBTRACT", "local(u2) - u2" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12331,6 +15391,10 @@ "BINARY_SUBTRACT", "t1 - u1" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12355,6 +15419,10 @@ "BINARY_SUBTRACT", "t - b" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12367,6 +15435,10 @@ "CALL_FUNCTION", "local(u2)" ], + [ + "STORE_FAST", + "t2" + ], [ "LOAD_FAST", "t2" @@ -12451,6 +15523,30 @@ "BINARY_SUBSCR", "_time.localtime(u)[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "datetime" @@ -12527,6 +15623,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -12579,14 +15679,26 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" ], + [ + "LOAD_FAST", + "self" + ], [ "LOAD_FAST", "offset" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -12611,6 +15723,18 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "self" @@ -12635,6 +15759,18 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12819,6 +15955,10 @@ "LOAD_ATTR", "self.year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -12835,6 +15975,10 @@ "LOAD_ATTR", "self.month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -12851,6 +15995,10 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "hour" @@ -12867,6 +16015,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -12883,6 +16035,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -12899,6 +16055,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -12915,6 +16075,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -12931,6 +16095,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -12947,6 +16115,10 @@ "LOAD_ATTR", "self.fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -13023,6 +16195,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_FAST", "self" @@ -13047,6 +16223,10 @@ "BINARY_FLOOR_DIVIDE", "(self - _EPOCH) // timedelta(seconds=1)" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_GLOBAL", "_time" @@ -13063,6 +16243,10 @@ "CALL_METHOD", "_time.localtime(ts)" ], + [ + "STORE_FAST", + "localtm" + ], [ "LOAD_GLOBAL", "datetime" @@ -13079,6 +16263,10 @@ "CALL_FUNCTION_EX", "datetime(*localtm[:6])" ], + [ + "STORE_FAST", + "local" + ], [ "LOAD_FAST", "localtm" @@ -13087,6 +16275,10 @@ "LOAD_ATTR", "localtm.tm_gmtoff" ], + [ + "STORE_FAST", + "gmtoff" + ], [ "LOAD_FAST", "localtm" @@ -13095,6 +16287,10 @@ "LOAD_ATTR", "localtm.tm_zone" ], + [ + "STORE_FAST", + "zone" + ], [ "LOAD_GLOBAL", "timezone" @@ -13139,6 +16335,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_GLOBAL", "isinstance" @@ -13171,6 +16371,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13191,6 +16395,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13207,6 +16415,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "mytz" @@ -13223,6 +16435,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "myoffset" @@ -13251,6 +16467,10 @@ "CALL_METHOD", "self.replace(tzinfo=None)._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13267,6 +16487,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "tz" @@ -13307,6 +16531,10 @@ "CALL_FUNCTION_KW", "(self - myoffset).replace(tzinfo=tz)" ], + [ + "STORE_FAST", + "utc" + ], [ "LOAD_FAST", "tz" @@ -13339,6 +16567,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -13491,6 +16723,10 @@ "BINARY_ADD", "\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13503,6 +16739,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -13515,14 +16755,26 @@ "CALL_FUNCTION", "_format_offset(off)" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -13583,6 +16835,10 @@ "LOAD_ATTR", "self._microsecond" ], + [ + "STORE_FAST", + "L" + ], [ "LOAD_FAST", "L" @@ -13599,6 +16855,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "L" @@ -13615,6 +16875,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "self" @@ -13667,6 +16931,10 @@ "BINARY_MODULO", "\"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13719,6 +16987,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13751,6 +17023,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -13767,6 +17043,10 @@ "CALL_FUNCTION_KW", "self.isoformat(sep=' ')" ], + [ + "STORE_FAST", + "import _strptime" + ], [ "LOAD_FAST", "_strptime" @@ -13823,6 +17103,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -13871,6 +17155,10 @@ "CALL_METHOD", "self._tzinfo.tzname(self)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -13919,6 +17207,10 @@ "CALL_METHOD", "self._tzinfo.dst(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -14303,6 +17595,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -14311,6 +17607,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -14323,6 +17631,10 @@ "IS_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -14335,6 +17647,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -14347,6 +17663,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "allow_mixed" @@ -14443,6 +17763,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -14607,6 +17931,10 @@ "BINARY_SUBTRACT", "self - other" ], + [ + "STORE_FAST", + "diff" + ], [ "LOAD_FAST", "diff" @@ -14695,10 +18023,22 @@ "CALL_FUNCTION_KW", "timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)" ], + [ + "STORE_FAST", + "delta" + ], + [ + "LOAD_FAST", + "delta" + ], [ "LOAD_FAST", "other" ], + [ + "STORE_FAST", + "delta" + ], [ "LOAD_GLOBAL", "divmod" @@ -14715,6 +18055,14 @@ "CALL_FUNCTION", "divmod(delta.seconds, 3600)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "rem" + ], [ "LOAD_GLOBAL", "divmod" @@ -14727,6 +18075,14 @@ "CALL_FUNCTION", "divmod(rem, 60)" ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "delta" @@ -14735,10 +18091,18 @@ "LOAD_ATTR", "delta.days" ], + [ + "COMPARE_OP", + "0 < delta.days <= _MAXORDINAL" + ], [ "LOAD_GLOBAL", "_MAXORDINAL" ], + [ + "COMPARE_OP", + "0 < delta.days <= _MAXORDINAL" + ], [ "LOAD_GLOBAL", "OverflowError" @@ -14895,6 +18259,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -14907,6 +18275,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_FAST", "self" @@ -14947,6 +18319,10 @@ "BINARY_ADD", "self._second + self._minute * 60 + self._hour * 3600" ], + [ + "STORE_FAST", + "secs1" + ], [ "LOAD_FAST", "other" @@ -14987,6 +18363,10 @@ "BINARY_ADD", "other._second + other._minute * 60 + other._hour * 3600" ], + [ + "STORE_FAST", + "secs2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -15039,6 +18419,10 @@ "CALL_FUNCTION", "timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)" ], + [ + "STORE_FAST", + "base" + ], [ "LOAD_FAST", "self" @@ -15075,6 +18459,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -15087,6 +18475,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -15179,10 +18571,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -15195,6 +18595,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -15275,6 +18679,10 @@ "CALL_FUNCTION", "_ymd2ord(self.year, self.month, self.day)" ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_FAST", "self" @@ -15315,6 +18723,10 @@ "BINARY_ADD", "self.hour * 3600 + self.minute * 60 + self.second" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_GLOBAL", "hash" @@ -15387,6 +18799,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "divmod" @@ -15403,6 +18823,14 @@ "CALL_FUNCTION", "divmod(self._microsecond, 256)" ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_GLOBAL", "divmod" @@ -15415,6 +18843,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -15423,6 +18859,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15439,6 +18879,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "m" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_GLOBAL", "bytes" @@ -15503,6 +18951,10 @@ "CALL_FUNCTION", "bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -15567,6 +19019,18 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15599,6 +19063,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "m" @@ -15759,6 +19235,10 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "STORE_FAST", + "THURSDAY" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -15771,6 +19251,10 @@ "CALL_FUNCTION", "_ymd2ord(year, 1, 1)" ], + [ + "STORE_FAST", + "firstday" + ], [ "LOAD_FAST", "firstday" @@ -15783,6 +19267,10 @@ "BINARY_MODULO", "(firstday + 6) % 7" ], + [ + "STORE_FAST", + "firstweekday" + ], [ "LOAD_FAST", "firstday" @@ -15795,6 +19283,10 @@ "BINARY_SUBTRACT", "firstday - firstweekday" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_FAST", "firstweekday" @@ -15811,6 +19303,30 @@ "LOAD_FAST", "week1monday" ], + [ + "STORE_FAST", + "week1monday" + ], + [ + "LOAD_FAST", + "week1monday" + ], + [ + "LOAD_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "__slots__" + ], [ "LOAD_NAME", "object" @@ -15819,10 +19335,18 @@ "CALL_FUNCTION", "object()" ], + [ + "STORE_NAME", + "_Omitted" + ], [ "LOAD_NAME", "_Omitted" ], + [ + "STORE_NAME", + " def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)" + ], [ "LOAD_NAME", "classmethod" @@ -15831,6 +19355,46 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self" + ], + [ + "STORE_NAME", + " def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __hash__(self):\n return hash(self._offset)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __str__(self):\n return self.tzname(None)" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")" + ], [ "LOAD_NAME", "timedelta" @@ -15839,6 +19403,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=24, microseconds=-1)" ], + [ + "STORE_NAME", + "_maxoffset" + ], [ "LOAD_NAME", "_maxoffset" @@ -15847,6 +19415,10 @@ "UNARY_NEGATIVE", "-_maxoffset" ], + [ + "STORE_NAME", + "_minoffset" + ], [ "LOAD_NAME", "staticmethod" @@ -15855,6 +19427,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15907,6 +19483,10 @@ "LOAD_ATTR", "cls.utc" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15943,6 +19523,10 @@ "LOAD_FAST", "offset" ], + [ + "COMPARE_OP", + "cls._minoffset <= offset <= cls._maxoffset" + ], [ "LOAD_FAST", "cls" @@ -15951,6 +19535,10 @@ "LOAD_ATTR", "cls._maxoffset" ], + [ + "COMPARE_OP", + "cls._minoffset <= offset <= cls._maxoffset" + ], [ "LOAD_GLOBAL", "ValueError" @@ -16003,6 +19591,10 @@ "CALL_METHOD", "tzinfo.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "offset" @@ -16471,6 +20063,10 @@ "COMPARE_OP", "delta < timedelta(0)" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "delta" @@ -16479,6 +20075,14 @@ "UNARY_NEGATIVE", "-delta" ], + [ + "STORE_FAST", + "delta" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -16499,6 +20103,14 @@ "CALL_FUNCTION", "divmod(delta, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hours" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -16519,6 +20131,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "minutes" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -16527,6 +20147,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "rest" @@ -16535,6 +20159,10 @@ "LOAD_ATTR", "rest.microseconds" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_FAST", "microseconds" @@ -16559,6 +20187,10 @@ "LOAD_FAST", "microseconds" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}'" + ], [ "LOAD_FAST", "seconds" @@ -16579,6 +20211,10 @@ "LOAD_FAST", "seconds" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'" + ], [ "LOAD_FAST", "sign" @@ -16590,5 +20226,9 @@ [ "LOAD_FAST", "minutes" + ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}'" ] ] \ No newline at end of file diff --git a/tests/sample_results/datetime-py-3.8.json b/tests/sample_results/datetime-py-3.8.json index 8066601..febd5d7 100644 --- a/tests/sample_results/datetime-py-3.8.json +++ b/tests/sample_results/datetime-py-3.8.json @@ -1,4 +1,56 @@ [ + [ + "STORE_NAME", + "\"\"\"Concrete date/time and related types.\n\nSee http://www.iana.org/time-zones/repository/tz-link.html for\ntime zone and DST data sources.\n\"\"\"" + ], + [ + "STORE_NAME", + "__all__" + ], + [ + "STORE_NAME", + "import time as _time" + ], + [ + "STORE_NAME", + "import math as _math" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from operator import index as _index" + ], + [ + "STORE_NAME", + "def _cmp(x, y):\n return 0 if x == y else 1 if x > y else -1" + ], + [ + "STORE_NAME", + "MINYEAR" + ], + [ + "STORE_NAME", + "MAXYEAR" + ], + [ + "STORE_NAME", + "_MAXORDINAL" + ], + [ + "STORE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "STORE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "STORE_NAME", + "dbm" + ], [ "LOAD_NAME", "_DAYS_IN_MONTH" @@ -7,6 +59,10 @@ "BINARY_SUBSCR", "_DAYS_IN_MONTH[1:]" ], + [ + "STORE_NAME", + "dim" + ], [ "LOAD_NAME", "_DAYS_BEFORE_MONTH" @@ -25,8 +81,44 @@ ], [ "LOAD_NAME", + "dbm += dim" + ], + [ + "LOAD_NAME", + "dim" + ], + [ + "STORE_NAME", + "dbm += dim" + ], + [ + "DELETE_NAME", + "dbm" + ], + [ + "DELETE_NAME", "dim" ], + [ + "STORE_NAME", + "def _is_leap(year):\n \"year -> 1 if leap year, else 0.\"\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)" + ], + [ + "STORE_NAME", + "def _days_before_year(year):\n \"year -> number of days before January 1st of year.\"\n y = year - 1\n return y*365 + y//4 - y//100 + y//400" + ], + [ + "STORE_NAME", + "def _days_in_month(year, month):\n \"year, month -> number of days in that month in that year.\"\n assert 1 <= month <= 12, month\n if month == 2 and _is_leap(year):\n return 29\n return _DAYS_IN_MONTH[month]" + ], + [ + "STORE_NAME", + "def _days_before_month(year, month):\n \"year, month -> number of days in year preceding first day of month.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))" + ], + [ + "STORE_NAME", + "def _ymd2ord(year, month, day):\n \"year, month, day -> ordinal, considering 01-Jan-0001 as day 1.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n dim = _days_in_month(year, month)\n assert 1 <= day <= dim, ('day must be in 1..%d' % dim)\n return (_days_before_year(year) +\n _days_before_month(year, month) +\n day)" + ], [ "LOAD_NAME", "_days_before_year" @@ -35,6 +127,10 @@ "CALL_FUNCTION", "_days_before_year(401)" ], + [ + "STORE_NAME", + "_DI400Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -43,6 +139,10 @@ "CALL_FUNCTION", "_days_before_year(101)" ], + [ + "STORE_NAME", + "_DI100Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -51,6 +151,10 @@ "CALL_FUNCTION", "_days_before_year(5)" ], + [ + "STORE_NAME", + "_DI4Y" + ], [ "LOAD_NAME", "_DI4Y" @@ -99,6 +203,98 @@ "COMPARE_OP", "_DI100Y == 25 * _DI4Y - 1" ], + [ + "STORE_NAME", + "def _ord2ymd(n):\n \"ordinal -> (year, month, day), considering 01-Jan-0001 as day 1.\"\n\n # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years\n # repeats exactly every 400 years. The basic strategy is to find the\n # closest 400-year boundary at or before n, then work with the offset\n # from that boundary to n. Life is much clearer if we subtract 1 from\n # n first -- then the values of n at 400-year boundaries are exactly\n # those divisible by _DI400Y:\n #\n # D M Y n n-1\n # -- --- ---- ---------- ----------------\n # 31 Dec -400 -_DI400Y -_DI400Y -1\n # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary\n # ...\n # 30 Dec 000 -1 -2\n # 31 Dec 000 0 -1\n # 1 Jan 001 1 0 400-year boundary\n # 2 Jan 001 2 1\n # 3 Jan 001 3 2\n # ...\n # 31 Dec 400 _DI400Y _DI400Y -1\n # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary\n n -= 1\n n400, n = divmod(n, _DI400Y)\n year = n400 * 400 + 1 # ..., -399, 1, 401, ...\n\n # Now n is the (non-negative) offset, in days, from January 1 of year, to\n # the desired date. Now compute how many 100-year cycles precede n.\n # Note that it's possible for n100 to equal 4! In that case 4 full\n # 100-year cycles precede the desired day, which implies the desired\n # day is December 31 at the end of a 400-year cycle.\n n100, n = divmod(n, _DI100Y)\n\n # Now compute how many 4-year cycles precede it.\n n4, n = divmod(n, _DI4Y)\n\n # And now how many single years. Again n1 can be 4, and again meaning\n # that the desired day is December 31 at the end of the 4-year cycle.\n n1, n = divmod(n, 365)\n\n year += n100 * 100 + n4 * 4 + n1\n if n1 == 4 or n100 == 4:\n assert n == 0\n return year-1, 12, 31\n\n # Now the year is correct, and n is the offset from January 1. We find\n # the month via an estimate that's either exact or one too large.\n leapyear = n1 == 3 and (n4 != 24 or n100 == 3)\n assert leapyear == _is_leap(year)\n month = (n + 50) >> 5\n preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)\n if preceding > n: # estimate is too large\n month -= 1\n preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)\n n -= preceding\n assert 0 <= n < _days_in_month(year, month)\n\n # Now the year and month are correct, and n is the offset from the\n # start of that month: we're done!\n return year, month, n+1" + ], + [ + "STORE_NAME", + "_MONTHNAMES" + ], + [ + "STORE_NAME", + "_DAYNAMES" + ], + [ + "STORE_NAME", + "def _build_struct_time(y, m, d, hh, mm, ss, dstflag):\n wday = (_ymd2ord(y, m, d) + 6) % 7\n dnum = _days_before_month(y, m) + d\n return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" + ], + [ + "STORE_NAME", + "def _format_time(hh, mm, ss, us, timespec='auto'):\n specs = {\n 'hours': '{:02d}',\n 'minutes': '{:02d}:{:02d}',\n 'seconds': '{:02d}:{:02d}:{:02d}',\n 'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',\n 'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'\n }\n\n if timespec == 'auto':\n # Skip trailing microseconds when us==0.\n timespec = 'microseconds' if us else 'seconds'\n elif timespec == 'milliseconds':\n us //= 1000\n try:\n fmt = specs[timespec]\n except KeyError:\n raise ValueError('Unknown timespec value')\n else:\n return fmt.format(hh, mm, ss, us)" + ], + [ + "STORE_NAME", + "def _format_offset(off):\n s = ''\n if off is not None:\n if off.days < 0:\n sign = \"-\"\n off = -off\n else:\n sign = \"+\"\n hh, mm = divmod(off, timedelta(hours=1))\n mm, ss = divmod(mm, timedelta(minutes=1))\n s += \"%s%02d:%02d\" % (sign, hh, mm)\n if ss or ss.microseconds:\n s += \":%02d\" % ss.seconds\n\n if ss.microseconds:\n s += '.%06d' % ss.microseconds\n return s" + ], + [ + "STORE_NAME", + "def _wrap_strftime(object, format, timetuple):\n # Don't call utcoffset() or tzname() unless actually needed.\n freplace = None # the string to use for %f\n zreplace = None # the string to use for %z\n Zreplace = None # the string to use for %Z\n\n # Scan format for %z and %Z escapes, replacing as needed.\n newformat = []\n push = newformat.append\n i, n = 0, len(format)\n while i < n:\n ch = format[i]\n i += 1\n if ch == '%':\n if i < n:\n ch = format[i]\n i += 1\n if ch == 'f':\n if freplace is None:\n freplace = '%06d' % getattr(object,\n 'microsecond', 0)\n newformat.append(freplace)\n elif ch == 'z':\n if zreplace is None:\n zreplace = \"\"\n if hasattr(object, \"utcoffset\"):\n offset = object.utcoffset()\n if offset is not None:\n sign = '+'\n if offset.days < 0:\n offset = -offset\n sign = '-'\n h, rest = divmod(offset, timedelta(hours=1))\n m, rest = divmod(rest, timedelta(minutes=1))\n s = rest.seconds\n u = offset.microseconds\n if u:\n zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u)\n elif s:\n zreplace = '%c%02d%02d%02d' % (sign, h, m, s)\n else:\n zreplace = '%c%02d%02d' % (sign, h, m)\n assert '%' not in zreplace\n newformat.append(zreplace)\n elif ch == 'Z':\n if Zreplace is None:\n Zreplace = \"\"\n if hasattr(object, \"tzname\"):\n s = object.tzname()\n if s is not None:\n # strftime is going to have at this: escape %\n Zreplace = s.replace('%', '%%')\n newformat.append(Zreplace)\n else:\n push('%')\n push(ch)\n else:\n push('%')\n else:\n push(ch)\n newformat = \"\".join(newformat)\n return _time.strftime(newformat, timetuple)" + ], + [ + "STORE_NAME", + "def _is_ascii_digit(c):\n return c in \"0123456789\"" + ], + [ + "STORE_NAME", + "def _find_isoformat_datetime_separator(dtstr):\n # See the comment in _datetimemodule.c:_find_isoformat_datetime_separator\n len_dtstr = len(dtstr)\n if len_dtstr == 7:\n return 7\n\n assert len_dtstr > 7\n date_separator = \"-\"\n week_indicator = \"W\"\n\n if dtstr[4] == date_separator:\n if dtstr[5] == week_indicator:\n if len_dtstr < 8:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 8 and dtstr[8] == date_separator:\n if len_dtstr == 9:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):\n # This is as far as we need to resolve the ambiguity for\n # the moment - if we have YYYY-Www-##, the separator is\n # either a hyphen at 8 or a number at 10.\n #\n # We'll assume it's a hyphen at 8 because it's way more\n # likely that someone will use a hyphen as a separator than\n # a number, but at this point it's really best effort\n # because this is an extension of the spec anyway.\n # TODO(pganssle): Document this\n return 8\n return 10\n else:\n # YYYY-Www (8)\n return 8\n else:\n # YYYY-MM-DD (10)\n return 10\n else:\n if dtstr[4] == week_indicator:\n # YYYYWww (7) or YYYYWwwd (8)\n idx = 7\n while idx < len_dtstr:\n if not _is_ascii_digit(dtstr[idx]):\n break\n idx += 1\n\n if idx < 9:\n return idx\n\n if idx % 2 == 0:\n # If the index of the last number is even, it's YYYYWwwd\n return 7\n else:\n return 8\n else:\n # YYYYMMDD (8)\n return 8" + ], + [ + "STORE_NAME", + "def _parse_isoformat_date(dtstr):\n # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,\n # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator\n assert len(dtstr) in (7, 8, 10)\n year = int(dtstr[0:4])\n has_sep = dtstr[4] == '-'\n\n pos = 4 + has_sep\n if dtstr[pos:pos + 1] == \"W\":\n # YYYY-?Www-?D?\n pos += 1\n weekno = int(dtstr[pos:pos + 2])\n pos += 2\n\n dayno = 1\n if len(dtstr) > pos:\n if (dtstr[pos:pos + 1] == '-') != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n\n dayno = int(dtstr[pos:pos + 1])\n\n return list(_isoweek_to_gregorian(year, weekno, dayno))\n else:\n month = int(dtstr[pos:pos + 2])\n pos += 2\n if (dtstr[pos:pos + 1] == \"-\") != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n day = int(dtstr[pos:pos + 2])\n\n return [year, month, day]" + ], + [ + "STORE_NAME", + "_FRACTION_CORRECTION" + ], + [ + "STORE_NAME", + "def _parse_hh_mm_ss_ff(tstr):\n # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]\n len_str = len(tstr)\n\n time_comps = [0, 0, 0, 0]\n pos = 0\n for comp in range(0, 3):\n if (len_str - pos) < 2:\n raise ValueError(\"Incomplete time component\")\n\n time_comps[comp] = int(tstr[pos:pos+2])\n\n pos += 2\n next_char = tstr[pos:pos+1]\n\n if comp == 0:\n has_sep = next_char == ':'\n\n if not next_char or comp >= 2:\n break\n\n if has_sep and next_char != ':':\n raise ValueError(\"Invalid time separator: %c\" % next_char)\n\n pos += has_sep\n\n if pos < len_str:\n if tstr[pos] not in '.,':\n raise ValueError(\"Invalid microsecond component\")\n else:\n pos += 1\n\n len_remainder = len_str - pos\n\n if len_remainder >= 6:\n to_parse = 6\n else:\n to_parse = len_remainder\n\n time_comps[3] = int(tstr[pos:(pos+to_parse)])\n if to_parse < 6:\n time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]\n if (len_remainder > to_parse\n and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))):\n raise ValueError(\"Non-digit values in unparsed fraction\")\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _parse_isoformat_time(tstr):\n # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]\n len_str = len(tstr)\n if len_str < 2:\n raise ValueError(\"Isoformat time too short\")\n\n # This is equivalent to re.search('[+-Z]', tstr), but faster\n tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1)\n timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr\n\n time_comps = _parse_hh_mm_ss_ff(timestr)\n\n tzi = None\n if tz_pos == len_str and tstr[-1] == 'Z':\n tzi = timezone.utc\n elif tz_pos > 0:\n tzstr = tstr[tz_pos:]\n\n # Valid time zone strings are:\n # HH len: 2\n # HHMM len: 4\n # HH:MM len: 5\n # HHMMSS len: 6\n # HHMMSS.f+ len: 7+\n # HH:MM:SS len: 8\n # HH:MM:SS.f+ len: 10+\n\n if len(tzstr) in (0, 1, 3):\n raise ValueError(\"Malformed time zone string\")\n\n tz_comps = _parse_hh_mm_ss_ff(tzstr)\n\n if all(x == 0 for x in tz_comps):\n tzi = timezone.utc\n else:\n tzsign = -1 if tstr[tz_pos - 1] == '-' else 1\n\n td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])\n\n tzi = timezone(tzsign * td)\n\n time_comps.append(tzi)\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _isoweek_to_gregorian(year, week, day):\n # Year is bounded this way because 9999-12-31 is (9999, 52, 5)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError(f\"Year is out of range: {year}\")\n\n if not 0 < week < 53:\n out_of_range = True\n\n if week == 53:\n # ISO years have 53 weeks in them on years starting with a\n # Thursday and leap years starting on a Wednesday\n first_weekday = _ymd2ord(year, 1, 1) % 7\n if (first_weekday == 4 or (first_weekday == 3 and\n _is_leap(year))):\n out_of_range = False\n\n if out_of_range:\n raise ValueError(f\"Invalid week: {week}\")\n\n if not 0 < day < 8:\n raise ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")\n\n # Now compute the offset from (Y, 1, 1) in days:\n day_offset = (week - 1) * 7 + (day - 1)\n\n # Calculate the ordinal day for monday, week 1\n day_1 = _isoweek1monday(year)\n ord_day = day_1 + day_offset\n\n return _ord2ymd(ord_day)" + ], + [ + "STORE_NAME", + "def _check_tzname(name):\n if name is not None and not isinstance(name, str):\n raise TypeError(\"tzinfo.tzname() must return None or string, \"\n \"not '%s'\" % type(name))" + ], + [ + "STORE_NAME", + "def _check_utc_offset(name, offset):\n assert name in (\"utcoffset\", \"dst\")\n if offset is None:\n return\n if not isinstance(offset, timedelta):\n raise TypeError(\"tzinfo.%s() must return None \"\n \"or timedelta, not '%s'\" % (name, type(offset)))\n if not -timedelta(1) < offset < timedelta(1):\n raise ValueError(\"%s()=%s, must be strictly between \"\n \"-timedelta(hours=24) and timedelta(hours=24)\" %\n (name, offset))" + ], + [ + "STORE_NAME", + "def _check_date_fields(year, month, day):\n year = _index(year)\n month = _index(month)\n day = _index(day)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)\n if not 1 <= month <= 12:\n raise ValueError('month must be in 1..12', month)\n dim = _days_in_month(year, month)\n if not 1 <= day <= dim:\n raise ValueError('day must be in 1..%d' % dim, day)\n return year, month, day" + ], + [ + "STORE_NAME", + "def _check_time_fields(hour, minute, second, microsecond, fold):\n hour = _index(hour)\n minute = _index(minute)\n second = _index(second)\n microsecond = _index(microsecond)\n if not 0 <= hour <= 23:\n raise ValueError('hour must be in 0..23', hour)\n if not 0 <= minute <= 59:\n raise ValueError('minute must be in 0..59', minute)\n if not 0 <= second <= 59:\n raise ValueError('second must be in 0..59', second)\n if not 0 <= microsecond <= 999999:\n raise ValueError('microsecond must be in 0..999999', microsecond)\n if fold not in (0, 1):\n raise ValueError('fold must be either 0 or 1', fold)\n return hour, minute, second, microsecond, fold" + ], + [ + "STORE_NAME", + "def _check_tzinfo_arg(tz):\n if tz is not None and not isinstance(tz, tzinfo):\n raise TypeError(\"tzinfo argument must be None or of a tzinfo subclass\")" + ], + [ + "STORE_NAME", + "def _cmperror(x, y):\n raise TypeError(\"can't compare '%s' to '%s'\" % (\n type(x).__name__, type(y).__name__))" + ], + [ + "STORE_NAME", + "def _divide_and_round(a, b):\n \"\"\"divide a by b and round result to the nearest integer\n\n When the ratio is exactly half-way between two integers,\n the even integer is returned.\n \"\"\"\n # Based on the reference implementation for divmod_near\n # in Objects/longobject.c.\n q, r = divmod(a, b)\n # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.\n # The expression r / b > 0.5 is equivalent to 2 * r > b if b is\n # positive, 2 * r < b if b negative.\n r *= 2\n greater_than_half = r > b if b > 0 else r < b\n if greater_than_half or r == b and q % 2 == 1:\n q += 1\n\n return q" + ], + [ + "CALL_FUNCTION", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "timedelta" @@ -147,10 +343,22 @@ "STORE_ATTR", "timedelta.resolution" ], + [ + "CALL_FUNCTION", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "date" ], + [ + "STORE_NAME", + "_date_class" + ], [ "LOAD_NAME", "date" @@ -199,22 +407,62 @@ "STORE_ATTR", "date.resolution" ], + [ + "CALL_FUNCTION", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_NAME", "tuple" ], + [ + "CALL_FUNCTION", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_NAME", "IsoCalendarDate" ], + [ + "STORE_NAME", + "_IsoCalendarDate" + ], + [ + "DELETE_NAME", + "IsoCalendarDate" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "STORE_NAME", + "_tzinfo_class" + ], + [ + "CALL_FUNCTION", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "time" ], + [ + "STORE_NAME", + "_time_class" + ], [ "LOAD_NAME", "time" @@ -267,6 +515,14 @@ "LOAD_NAME", "date" ], + [ + "CALL_FUNCTION", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "datetime" @@ -315,10 +571,22 @@ "STORE_ATTR", "datetime.resolution" ], + [ + "STORE_NAME", + "def _isoweek1monday(year):\n # Helper to calculate the day number of the Monday starting week 1\n # XXX This could be done more efficiently\n THURSDAY = 3\n firstday = _ymd2ord(year, 1, 1)\n firstweekday = (firstday + 6) % 7 # See weekday() above\n week1monday = firstday - firstweekday\n if firstweekday > THURSDAY:\n week1monday += 7\n return week1monday" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "CALL_FUNCTION", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_NAME", "timezone" @@ -339,6 +607,10 @@ "CALL_METHOD", "timezone._create(timedelta(0))" ], + [ + "STORE_NAME", + "UTC" + ], [ "LOAD_NAME", "timezone" @@ -423,10 +695,186 @@ "CALL_FUNCTION_KW", "datetime(1970, 1, 1, tzinfo=timezone.utc)" ], + [ + "STORE_NAME", + "_EPOCH" + ], [ "LOAD_NAME", "ImportError" ], + [ + "DELETE_NAME", + "_DAYNAMES" + ], + [ + "DELETE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "DELETE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "DELETE_NAME", + "_DI100Y" + ], + [ + "DELETE_NAME", + "_DI400Y" + ], + [ + "DELETE_NAME", + "_DI4Y" + ], + [ + "DELETE_NAME", + "_EPOCH" + ], + [ + "DELETE_NAME", + "_MAXORDINAL" + ], + [ + "DELETE_NAME", + "_MONTHNAMES" + ], + [ + "DELETE_NAME", + "_build_struct_time" + ], + [ + "DELETE_NAME", + "_check_date_fields" + ], + [ + "DELETE_NAME", + "_check_time_fields" + ], + [ + "DELETE_NAME", + "_check_tzinfo_arg" + ], + [ + "DELETE_NAME", + "_check_tzname" + ], + [ + "DELETE_NAME", + "_check_utc_offset" + ], + [ + "DELETE_NAME", + "_cmp" + ], + [ + "DELETE_NAME", + "_cmperror" + ], + [ + "DELETE_NAME", + "_date_class" + ], + [ + "DELETE_NAME", + "_days_before_month" + ], + [ + "DELETE_NAME", + "_days_before_year" + ], + [ + "DELETE_NAME", + "_days_in_month" + ], + [ + "DELETE_NAME", + "_format_time" + ], + [ + "DELETE_NAME", + "_format_offset" + ], + [ + "DELETE_NAME", + "_index" + ], + [ + "DELETE_NAME", + "_is_leap" + ], + [ + "DELETE_NAME", + "_isoweek1monday" + ], + [ + "DELETE_NAME", + "_math" + ], + [ + "DELETE_NAME", + "_ord2ymd" + ], + [ + "DELETE_NAME", + "_time" + ], + [ + "DELETE_NAME", + "_time_class" + ], + [ + "DELETE_NAME", + "_tzinfo_class" + ], + [ + "DELETE_NAME", + "_wrap_strftime" + ], + [ + "DELETE_NAME", + "_ymd2ord" + ], + [ + "DELETE_NAME", + "_divide_and_round" + ], + [ + "DELETE_NAME", + "_parse_isoformat_date" + ], + [ + "DELETE_NAME", + "_parse_isoformat_time" + ], + [ + "DELETE_NAME", + "_parse_hh_mm_ss_ff" + ], + [ + "DELETE_NAME", + "_IsoCalendarDate" + ], + [ + "DELETE_NAME", + "_isoweek_to_gregorian" + ], + [ + "DELETE_NAME", + "_find_isoformat_datetime_separator" + ], + [ + "DELETE_NAME", + "_FRACTION_CORRECTION" + ], + [ + "DELETE_NAME", + "_is_ascii_digit" + ], + [ + "STORE_NAME", + "from _datetime import __doc__" + ], [ "LOAD_FAST", "x" @@ -495,6 +943,10 @@ "BINARY_SUBTRACT", "year - 1" ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_FAST", "y" @@ -639,6 +1091,10 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" @@ -695,6 +1151,14 @@ "BINARY_ADD", "_days_before_year(year) +\n _days_before_month(year, month) +\n day" ], + [ + "LOAD_FAST", + "n -= 1" + ], + [ + "STORE_FAST", + "n -= 1" + ], [ "LOAD_GLOBAL", "divmod" @@ -711,6 +1175,14 @@ "CALL_FUNCTION", "divmod(n, _DI400Y)" ], + [ + "STORE_FAST", + "n400" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n400" @@ -723,6 +1195,10 @@ "BINARY_ADD", "n400 * 400 + 1" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "divmod" @@ -739,6 +1215,14 @@ "CALL_FUNCTION", "divmod(n, _DI100Y)" ], + [ + "STORE_FAST", + "n100" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -755,6 +1239,14 @@ "CALL_FUNCTION", "divmod(n, _DI4Y)" ], + [ + "STORE_FAST", + "n4" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -767,6 +1259,18 @@ "CALL_FUNCTION", "divmod(n, 365)" ], + [ + "STORE_FAST", + "n1" + ], + [ + "STORE_FAST", + "n" + ], + [ + "LOAD_FAST", + "year += n100 * 100 + n4 * 4 + n1" + ], [ "LOAD_FAST", "n100" @@ -795,6 +1299,10 @@ "BINARY_ADD", "n100 * 100 + n4 * 4 + n1" ], + [ + "STORE_FAST", + "year += n100 * 100 + n4 * 4 + n1" + ], [ "LOAD_FAST", "n1" @@ -851,6 +1359,10 @@ "COMPARE_OP", "n100 == 3" ], + [ + "STORE_FAST", + "leapyear" + ], [ "LOAD_FAST", "leapyear" @@ -883,6 +1395,10 @@ "BINARY_RSHIFT", "(n + 50) >> 5" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_DAYS_BEFORE_MONTH" @@ -911,17 +1427,33 @@ "BINARY_ADD", "_DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding" + ], [ "LOAD_FAST", "preceding" ], [ - "LOAD_FAST", - "n" + "LOAD_FAST", + "n" + ], + [ + "COMPARE_OP", + "preceding > n" + ], + [ + "LOAD_FAST", + "month -= 1" + ], + [ + "STORE_FAST", + "month -= 1" ], [ - "COMPARE_OP", - "preceding > n" + "LOAD_FAST", + "preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)" ], [ "LOAD_GLOBAL", @@ -951,10 +1483,22 @@ "BINARY_ADD", "_DAYS_IN_MONTH[month] + (month == 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)" + ], + [ + "LOAD_FAST", + "n -= preceding" + ], [ "LOAD_FAST", "preceding" ], + [ + "STORE_FAST", + "n -= preceding" + ], [ "LOAD_FAST", "n" @@ -1019,6 +1563,10 @@ "BINARY_MODULO", "(_ymd2ord(y, m, d) + 6) % 7" ], + [ + "STORE_FAST", + "wday" + ], [ "LOAD_GLOBAL", "_days_before_month" @@ -1043,6 +1591,10 @@ "BINARY_ADD", "_days_before_month(y, m) + d" ], + [ + "STORE_FAST", + "dnum" + ], [ "LOAD_GLOBAL", "_time" @@ -1091,6 +1643,10 @@ "CALL_METHOD", "_time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" ], + [ + "STORE_FAST", + "specs" + ], [ "LOAD_FAST", "timespec" @@ -1103,6 +1659,10 @@ "LOAD_FAST", "us" ], + [ + "STORE_FAST", + "timespec" + ], [ "LOAD_FAST", "timespec" @@ -1111,6 +1671,14 @@ "COMPARE_OP", "timespec == 'milliseconds'" ], + [ + "LOAD_FAST", + "us //= 1000" + ], + [ + "STORE_FAST", + "us //= 1000" + ], [ "LOAD_FAST", "specs" @@ -1123,6 +1691,10 @@ "BINARY_SUBSCR", "specs[timespec]" ], + [ + "STORE_FAST", + "fmt" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1163,6 +1735,10 @@ "CALL_METHOD", "fmt.format(hh, mm, ss, us)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "off" @@ -1183,6 +1759,10 @@ "COMPARE_OP", "off.days < 0" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "off" @@ -1191,6 +1771,14 @@ "UNARY_NEGATIVE", "-off" ], + [ + "STORE_FAST", + "off" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1211,6 +1799,14 @@ "CALL_FUNCTION", "divmod(off, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_GLOBAL", "divmod" @@ -1231,6 +1827,18 @@ "CALL_FUNCTION", "divmod(mm, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "LOAD_FAST", + "s += \"%s%02d:%02d\" % (sign, hh, mm)" + ], [ "LOAD_FAST", "sign" @@ -1247,6 +1855,10 @@ "BINARY_MODULO", "\"%s%02d:%02d\" % (sign, hh, mm)" ], + [ + "STORE_FAST", + "s += \"%s%02d:%02d\" % (sign, hh, mm)" + ], [ "LOAD_FAST", "ss" @@ -1259,6 +1871,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s += \":%02d\" % ss.seconds" + ], [ "LOAD_FAST", "ss" @@ -1271,6 +1887,10 @@ "BINARY_MODULO", "\":%02d\" % ss.seconds" ], + [ + "STORE_FAST", + "s += \":%02d\" % ss.seconds" + ], [ "LOAD_FAST", "ss" @@ -1279,6 +1899,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s += '.%06d' % ss.microseconds" + ], [ "LOAD_FAST", "ss" @@ -1291,10 +1915,30 @@ "BINARY_MODULO", "'.%06d' % ss.microseconds" ], + [ + "STORE_FAST", + "s += '.%06d' % ss.microseconds" + ], [ "LOAD_FAST", "s" ], + [ + "STORE_FAST", + "freplace" + ], + [ + "STORE_FAST", + "zreplace" + ], + [ + "STORE_FAST", + "Zreplace" + ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_FAST", "newformat" @@ -1303,6 +1947,10 @@ "LOAD_ATTR", "newformat.append" ], + [ + "STORE_FAST", + "push" + ], [ "LOAD_GLOBAL", "len" @@ -1315,6 +1963,14 @@ "CALL_FUNCTION", "len(format)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "i" @@ -1339,6 +1995,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i += 1" + ], + [ + "STORE_FAST", + "i += 1" + ], [ "LOAD_FAST", "ch" @@ -1371,6 +2039,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i += 1" + ], + [ + "STORE_FAST", + "i += 1" + ], [ "LOAD_FAST", "ch" @@ -1403,6 +2083,10 @@ "BINARY_MODULO", "'%06d' % getattr(object,\n 'microsecond', 0)" ], + [ + "STORE_FAST", + "freplace" + ], [ "LOAD_FAST", "newformat" @@ -1435,6 +2119,10 @@ "COMPARE_OP", "zreplace is None" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1459,6 +2147,10 @@ "CALL_METHOD", "object.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" @@ -1467,6 +2159,10 @@ "COMPARE_OP", "offset is not None" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "offset" @@ -1487,6 +2183,14 @@ "UNARY_NEGATIVE", "-offset" ], + [ + "STORE_FAST", + "offset" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1507,6 +2211,14 @@ "CALL_FUNCTION", "divmod(offset, timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -1527,6 +2239,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -1535,6 +2255,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "offset" @@ -1543,6 +2267,10 @@ "LOAD_ATTR", "offset.microseconds" ], + [ + "STORE_FAST", + "u" + ], [ "LOAD_FAST", "u" @@ -1571,6 +2299,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d.%06d' % (sign, h, m, s, u)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "s" @@ -1595,6 +2327,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d' % (sign, h, m, s)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "sign" @@ -1611,6 +2347,10 @@ "BINARY_MODULO", "'%c%02d%02d' % (sign, h, m)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "zreplace" @@ -1651,6 +2391,10 @@ "COMPARE_OP", "Zreplace is None" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1675,6 +2419,10 @@ "CALL_METHOD", "object.tzname()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -1695,6 +2443,10 @@ "CALL_METHOD", "s.replace('%', '%%')" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_FAST", "newformat" @@ -1763,6 +2515,10 @@ "CALL_METHOD", "\"\".join(newformat)" ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_GLOBAL", "_time" @@ -1803,6 +2559,10 @@ "CALL_FUNCTION", "len(dtstr)" ], + [ + "STORE_FAST", + "len_dtstr" + ], [ "LOAD_FAST", "len_dtstr" @@ -1819,6 +2579,14 @@ "COMPARE_OP", "len_dtstr > 7" ], + [ + "STORE_FAST", + "date_separator" + ], + [ + "STORE_FAST", + "week_indicator" + ], [ "LOAD_FAST", "dtstr" @@ -1947,6 +2715,10 @@ "COMPARE_OP", "dtstr[4] == week_indicator" ], + [ + "STORE_FAST", + "idx" + ], [ "LOAD_FAST", "idx" @@ -1979,6 +2751,14 @@ "CALL_FUNCTION", "_is_ascii_digit(dtstr[idx])" ], + [ + "LOAD_FAST", + "idx += 1" + ], + [ + "STORE_FAST", + "idx += 1" + ], [ "LOAD_FAST", "idx" @@ -2035,6 +2815,10 @@ "CALL_FUNCTION", "int(dtstr[0:4])" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "dtstr" @@ -2047,6 +2831,10 @@ "COMPARE_OP", "dtstr[4] == '-'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "has_sep" @@ -2055,6 +2843,10 @@ "BINARY_ADD", "4 + has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "dtstr" @@ -2079,6 +2871,14 @@ "COMPARE_OP", "dtstr[pos:pos + 1] == \"W\"" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_GLOBAL", "int" @@ -2107,6 +2907,22 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "weekno" + ], + [ + "LOAD_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "len" @@ -2167,10 +2983,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos += has_sep" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos += has_sep" + ], [ "LOAD_GLOBAL", "int" @@ -2199,6 +3023,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 1])" ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "list" @@ -2255,6 +3083,18 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "month" + ], + [ + "LOAD_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "pos += 2" + ], [ "LOAD_FAST", "dtstr" @@ -2295,10 +3135,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos += has_sep" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos += has_sep" + ], [ "LOAD_GLOBAL", "int" @@ -2327,6 +3175,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "year" @@ -2351,6 +3203,18 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "range" @@ -2359,6 +3223,10 @@ "CALL_FUNCTION", "range(0, 3)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_FAST", "len_str" @@ -2423,6 +3291,14 @@ "STORE_SUBSCR", "time_comps[comp]" ], + [ + "LOAD_FAST", + "pos += 2" + ], + [ + "STORE_FAST", + "pos += 2" + ], [ "LOAD_FAST", "tstr" @@ -2443,6 +3319,10 @@ "BINARY_SUBSCR", "tstr[pos:pos+1]" ], + [ + "STORE_FAST", + "next_char" + ], [ "LOAD_FAST", "comp" @@ -2459,6 +3339,10 @@ "COMPARE_OP", "next_char == ':'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "next_char" @@ -2499,10 +3383,18 @@ "CALL_FUNCTION", "ValueError(\"Invalid time separator: %c\" % next_char)" ], + [ + "LOAD_FAST", + "pos += has_sep" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos += has_sep" + ], [ "LOAD_FAST", "pos" @@ -2539,6 +3431,14 @@ "CALL_FUNCTION", "ValueError(\"Invalid microsecond component\")" ], + [ + "LOAD_FAST", + "pos += 1" + ], + [ + "STORE_FAST", + "pos += 1" + ], [ "LOAD_FAST", "len_str" @@ -2551,6 +3451,10 @@ "BINARY_SUBTRACT", "len_str - pos" ], + [ + "STORE_FAST", + "len_remainder" + ], [ "LOAD_FAST", "len_remainder" @@ -2559,10 +3463,18 @@ "COMPARE_OP", "len_remainder >= 6" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_FAST", "len_remainder" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_GLOBAL", "int" @@ -2615,6 +3527,10 @@ "LOAD_FAST", "time_comps" ], + [ + "BINARY_SUBSCR", + "time_comps[3]" + ], [ "LOAD_GLOBAL", "_FRACTION_CORRECTION" @@ -2711,6 +3627,10 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], [ "LOAD_FAST", "len_str" @@ -2775,6 +3695,10 @@ "BINARY_ADD", "tstr.find('Z') + 1" ], + [ + "STORE_FAST", + "tz_pos" + ], [ "LOAD_FAST", "tz_pos" @@ -2803,6 +3727,10 @@ "LOAD_FAST", "tstr" ], + [ + "STORE_FAST", + "timestr" + ], [ "LOAD_GLOBAL", "_parse_hh_mm_ss_ff" @@ -2815,6 +3743,14 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(timestr)" ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2847,6 +3783,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2867,6 +3807,10 @@ "BINARY_SUBSCR", "tstr[tz_pos:]" ], + [ + "STORE_FAST", + "tzstr" + ], [ "LOAD_GLOBAL", "len" @@ -2903,6 +3847,10 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(tzstr)" ], + [ + "STORE_FAST", + "tz_comps" + ], [ "LOAD_GLOBAL", "all" @@ -2911,6 +3859,10 @@ "LOAD_FAST", "tz_comps" ], + [ + "CALL_FUNCTION", + "(x == 0 for x in tz_comps)" + ], [ "CALL_FUNCTION", "all(x == 0 for x in tz_comps)" @@ -2923,6 +3875,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tstr" @@ -2943,6 +3899,10 @@ "COMPARE_OP", "tstr[tz_pos - 1] == '-'" ], + [ + "STORE_FAST", + "tzsign" + ], [ "LOAD_GLOBAL", "timedelta" @@ -2983,6 +3943,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])" ], + [ + "STORE_FAST", + "td" + ], [ "LOAD_GLOBAL", "timezone" @@ -3003,6 +3967,10 @@ "CALL_FUNCTION", "timezone(tzsign * td)" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "time_comps" @@ -3023,6 +3991,14 @@ "LOAD_FAST", "time_comps" ], + [ + "LOAD_FAST", + "(x == 0 for x in tz_comps)" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -3051,6 +4027,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Year is out of range: {year}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Year is out of range: {year}\")" @@ -3059,6 +4039,10 @@ "LOAD_FAST", "week" ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "week" @@ -3083,6 +4067,10 @@ "BINARY_MODULO", "_ymd2ord(year, 1, 1) % 7" ], + [ + "STORE_FAST", + "first_weekday" + ], [ "LOAD_FAST", "first_weekday" @@ -3111,6 +4099,10 @@ "CALL_FUNCTION", "_is_leap(year)" ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "out_of_range" @@ -3123,6 +4115,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Invalid week: {week}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid week: {week}\")" @@ -3139,6 +4135,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f\"Invalid weekday: {day} (range is [1, 7])\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")" @@ -3167,6 +4167,10 @@ "BINARY_ADD", "(week - 1) * 7 + (day - 1)" ], + [ + "STORE_FAST", + "day_offset" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -3179,6 +4183,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "day_1" + ], [ "LOAD_FAST", "day_1" @@ -3191,6 +4199,10 @@ "BINARY_ADD", "day_1 + day_offset" ], + [ + "STORE_FAST", + "ord_day" + ], [ "LOAD_GLOBAL", "_ord2ymd" @@ -3367,6 +4379,10 @@ "CALL_FUNCTION", "_index(year)" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_index" @@ -3379,6 +4395,10 @@ "CALL_FUNCTION", "_index(month)" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_index" @@ -3391,6 +4411,10 @@ "CALL_FUNCTION", "_index(day)" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "MINYEAR" @@ -3459,6 +4483,10 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" @@ -3511,6 +4539,10 @@ "CALL_FUNCTION", "_index(hour)" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "_index" @@ -3523,6 +4555,10 @@ "CALL_FUNCTION", "_index(minute)" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_GLOBAL", "_index" @@ -3535,6 +4571,10 @@ "CALL_FUNCTION", "_index(second)" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_GLOBAL", "_index" @@ -3547,6 +4587,10 @@ "CALL_FUNCTION", "_index(microsecond)" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "hour" @@ -3743,6 +4787,22 @@ "CALL_FUNCTION", "divmod(a, b)" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], + [ + "LOAD_FAST", + "r *= 2" + ], + [ + "STORE_FAST", + "r *= 2" + ], [ "LOAD_FAST", "b" @@ -3752,92 +4812,264 @@ "b > 0" ], [ - "LOAD_FAST", - "r" + "LOAD_FAST", + "r" + ], + [ + "LOAD_FAST", + "b" + ], + [ + "COMPARE_OP", + "r > b" + ], + [ + "LOAD_FAST", + "r" + ], + [ + "LOAD_FAST", + "b" + ], + [ + "COMPARE_OP", + "r < b" + ], + [ + "STORE_FAST", + "greater_than_half" + ], + [ + "LOAD_FAST", + "greater_than_half" + ], + [ + "LOAD_FAST", + "r" + ], + [ + "LOAD_FAST", + "b" + ], + [ + "COMPARE_OP", + "r == b" + ], + [ + "LOAD_FAST", + "q" + ], + [ + "BINARY_MODULO", + "q % 2" + ], + [ + "COMPARE_OP", + "q % 2 == 1" + ], + [ + "LOAD_FAST", + "q += 1" + ], + [ + "STORE_FAST", + "q += 1" + ], + [ + "LOAD_FAST", + "q" + ], + [ + "LOAD_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))" + ], + [ + "STORE_NAME", + " def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s" + ], + [ + "STORE_NAME", + " def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6" + ], + [ + "LOAD_NAME", + "property" + ], + [ + "CALL_FUNCTION", + "property" + ], + [ + "STORE_NAME", + " @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days" + ], + [ + "LOAD_NAME", + "property" + ], + [ + "CALL_FUNCTION", + "property" + ], + [ + "STORE_NAME", + " @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds" + ], + [ + "LOAD_NAME", + "property" + ], + [ + "CALL_FUNCTION", + "property" + ], + [ + "STORE_NAME", + " @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented" + ], + [ + "LOAD_NAME", + "__add__" + ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)" + ], + [ + "STORE_NAME", + " def __pos__(self):\n return self" + ], + [ + "STORE_NAME", + " def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self" + ], + [ + "STORE_NAME", + " def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented" ], [ - "LOAD_FAST", - "b" + "LOAD_NAME", + "__mul__" ], [ - "COMPARE_OP", - "r > b" + "STORE_NAME", + "__rmul__" ], [ - "LOAD_FAST", - "r" + "STORE_NAME", + " def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)" ], [ - "LOAD_FAST", - "b" + "STORE_NAME", + " def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)" ], [ - "COMPARE_OP", - "r < b" + "STORE_NAME", + " def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))" ], [ - "LOAD_FAST", - "greater_than_half" + "STORE_NAME", + " def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented" ], [ - "LOAD_FAST", - "r" + "STORE_NAME", + " def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented" ], [ - "LOAD_FAST", - "b" + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented" ], [ - "COMPARE_OP", - "r == b" + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" ], [ - "LOAD_FAST", - "q" + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented" ], [ - "BINARY_MODULO", - "q % 2" + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" ], [ - "COMPARE_OP", - "q % 2 == 1" + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented" ], [ - "LOAD_FAST", - "q" + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())" ], [ - "LOAD_NAME", - "property" + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" ], [ - "CALL_FUNCTION", - "property" + "STORE_NAME", + " def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)" ], [ - "LOAD_NAME", - "property" + "STORE_NAME", + " def _getstate(self):\n return (self._days, self._seconds, self._microseconds)" ], [ - "CALL_FUNCTION", - "property" + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" ], [ - "LOAD_NAME", - "property" + "STORE_FAST", + "d" ], [ - "CALL_FUNCTION", - "property" + "STORE_FAST", + "s" ], [ - "LOAD_NAME", - "__add__" + "STORE_FAST", + "us" ], [ - "LOAD_NAME", - "__mul__" + "LOAD_FAST", + "days += weeks*7" ], [ "LOAD_FAST", @@ -3847,6 +5079,14 @@ "BINARY_MULTIPLY", "weeks*7" ], + [ + "STORE_FAST", + "days += weeks*7" + ], + [ + "LOAD_FAST", + "seconds += minutes*60 + hours*3600" + ], [ "LOAD_FAST", "minutes" @@ -3867,6 +5107,14 @@ "BINARY_ADD", "minutes*60 + hours*3600" ], + [ + "STORE_FAST", + "seconds += minutes*60 + hours*3600" + ], + [ + "LOAD_FAST", + "microseconds += milliseconds*1000" + ], [ "LOAD_FAST", "milliseconds" @@ -3875,6 +5123,10 @@ "BINARY_MULTIPLY", "milliseconds*1000" ], + [ + "STORE_FAST", + "microseconds += milliseconds*1000" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3907,6 +5159,14 @@ "CALL_METHOD", "_math.modf(days)" ], + [ + "STORE_FAST", + "dayfrac" + ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_GLOBAL", "_math" @@ -3927,6 +5187,14 @@ "CALL_METHOD", "_math.modf(dayfrac * (24.*3600.))" ], + [ + "STORE_FAST", + "daysecondsfrac" + ], + [ + "STORE_FAST", + "daysecondswhole" + ], [ "LOAD_FAST", "daysecondswhole" @@ -3959,6 +5227,10 @@ "CALL_FUNCTION", "int(daysecondswhole)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "days" @@ -3991,10 +5263,22 @@ "CALL_FUNCTION", "int(days)" ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "daysecondsfrac" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4091,6 +5375,14 @@ "CALL_METHOD", "_math.modf(seconds)" ], + [ + "STORE_FAST", + "secondsfrac" + ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "seconds" @@ -4123,10 +5415,22 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "secondsfrac += daysecondsfrac" + ], [ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac += daysecondsfrac" + ], [ "LOAD_GLOBAL", "abs" @@ -4147,6 +5451,10 @@ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4207,10 +5515,30 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d += days" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d += days" + ], + [ + "LOAD_FAST", + "s += int(seconds)" + ], [ "LOAD_GLOBAL", "int" @@ -4223,6 +5551,10 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "s += int(seconds)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4263,6 +5595,10 @@ "BINARY_MULTIPLY", "secondsfrac * 1e6" ], + [ + "STORE_FAST", + "usdouble" + ], [ "LOAD_GLOBAL", "abs" @@ -4315,6 +5651,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4327,6 +5667,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4339,14 +5687,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d += days" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d += days" + ], + [ + "LOAD_FAST", + "s += seconds" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s += seconds" + ], [ "LOAD_GLOBAL", "int" @@ -4359,6 +5731,10 @@ "CALL_FUNCTION", "int(microseconds)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4371,6 +5747,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4383,14 +5767,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d += days" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d += days" + ], + [ + "LOAD_FAST", + "s += seconds" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s += seconds" + ], [ "LOAD_GLOBAL", "round" @@ -4411,6 +5819,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4487,10 +5899,26 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "us" + ], + [ + "LOAD_FAST", + "s += seconds" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s += seconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4503,10 +5931,26 @@ "CALL_FUNCTION", "divmod(s, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "s" + ], + [ + "LOAD_FAST", + "d += days" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d += days" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4611,6 +6055,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "d" @@ -4659,6 +6107,10 @@ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -4827,6 +6279,14 @@ "CALL_FUNCTION", "divmod(self._seconds, 60)" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "divmod" @@ -4839,6 +6299,14 @@ "CALL_FUNCTION", "divmod(mm, 60)" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_FAST", "hh" @@ -4855,6 +6323,10 @@ "BINARY_MODULO", "\"%d:%02d:%02d\" % (hh, mm, ss)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4863,6 +6335,10 @@ "LOAD_ATTR", "self._days" ], + [ + "STORE_FAST", + " def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"" + ], [ "LOAD_FAST", "plural" @@ -4891,6 +6367,10 @@ "BINARY_ADD", "(\"%d day%s, \" % plural(self._days)) + s" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4919,6 +6399,10 @@ "BINARY_ADD", "s + \".%06d\" % self._microseconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -5395,6 +6879,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_FAST", "other" @@ -5407,6 +6895,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5519,6 +7015,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5631,6 +7131,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5735,6 +7239,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5811,6 +7323,10 @@ "BINARY_MODULO", "self._to_microseconds() % other._to_microseconds()" ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5875,6 +7391,14 @@ "CALL_FUNCTION", "divmod(self._to_microseconds(),\n other._to_microseconds())" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_FAST", "q" @@ -6271,6 +7795,30 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self" + ], [ "LOAD_NAME", "classmethod" @@ -6279,6 +7827,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6287,6 +7839,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -6295,6 +7851,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6303,6 +7863,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')" + ], [ "LOAD_NAME", "classmethod" @@ -6311,10 +7875,38 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)" + ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "property" @@ -6323,6 +7915,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year" + ], [ "LOAD_NAME", "property" @@ -6331,6 +7927,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month" + ], [ "LOAD_NAME", "property" @@ -6339,10 +7939,90 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)" + ], + [ + "STORE_NAME", + " def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7" + ], + [ + "STORE_NAME", + " def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7" + ], + [ + "STORE_NAME", + " def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)" + ], + [ + "STORE_NAME", + " def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day])," + ], + [ + "STORE_NAME", + " def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_FAST", "month" @@ -6431,6 +8111,10 @@ "CALL_METHOD", "year.encode('latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -6459,6 +8143,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -6507,6 +8195,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "object" @@ -6523,6 +8223,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -6587,6 +8291,42 @@ "CALL_METHOD", "_time.localtime(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "cls" @@ -6619,6 +8359,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -6647,6 +8391,18 @@ "CALL_FUNCTION", "_ord2ymd(n)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "cls" @@ -6715,6 +8471,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6751,6 +8511,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6851,6 +8615,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -7147,6 +8915,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -7163,6 +8935,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -7179,6 +8955,10 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "type" @@ -7447,6 +9227,18 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "other" @@ -7471,6 +9263,18 @@ "LOAD_ATTR", "other._day" ], + [ + "STORE_FAST", + "y2" + ], + [ + "STORE_FAST", + "m2" + ], + [ + "STORE_FAST", + "d2" + ], [ "LOAD_GLOBAL", "_cmp" @@ -7591,6 +9395,10 @@ "BINARY_ADD", "self.toordinal() + other.days" ], + [ + "STORE_FAST", + "o" + ], [ "LOAD_FAST", "o" @@ -7707,6 +9515,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -7719,6 +9531,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -7787,6 +9603,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7799,6 +9619,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -7831,6 +9655,10 @@ "CALL_FUNCTION", "_ymd2ord(self._year, self._month, self._day)" ], + [ + "STORE_FAST", + "today" + ], [ "LOAD_GLOBAL", "divmod" @@ -7851,6 +9679,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7859,6 +9695,14 @@ "COMPARE_OP", "week < 0" ], + [ + "LOAD_FAST", + "year -= 1" + ], + [ + "STORE_FAST", + "year -= 1" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7871,6 +9715,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "divmod" @@ -7891,6 +9739,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7923,6 +9779,18 @@ "COMPARE_OP", "today >= _isoweek1monday(year+1)" ], + [ + "LOAD_FAST", + "year += 1" + ], + [ + "STORE_FAST", + "year += 1" + ], + [ + "STORE_FAST", + "week" + ], [ "LOAD_GLOBAL", "_IsoCalendarDate" @@ -7967,6 +9835,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "bytes" @@ -8003,6 +9879,14 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_FAST", "self" @@ -8063,6 +9947,46 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "\"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_GLOBAL", "NotImplementedError" @@ -8147,6 +10071,10 @@ "CALL_METHOD", "dt.utcoffset()" ], + [ + "STORE_FAST", + "dtoff" + ], [ "LOAD_FAST", "dtoff" @@ -8175,6 +10103,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8203,14 +10135,26 @@ "BINARY_SUBTRACT", "dtoff - dtdst" ], + [ + "STORE_FAST", + "delta" + ], [ "LOAD_FAST", "delta" ], + [ + "LOAD_FAST", + "dt += delta" + ], [ "LOAD_FAST", "delta" ], + [ + "STORE_FAST", + "dt += delta" + ], [ "LOAD_FAST", "dt" @@ -8223,6 +10167,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8263,6 +10211,10 @@ "CALL_FUNCTION", "getattr(self, \"__getinitargs__\", None)" ], + [ + "STORE_FAST", + "getinitargs" + ], [ "LOAD_FAST", "getinitargs" @@ -8275,6 +10227,14 @@ "CALL_FUNCTION", "getinitargs()" ], + [ + "STORE_FAST", + "args" + ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -8299,6 +10259,22 @@ "CALL_METHOD", "self.__getstate__()" ], + [ + "LOAD_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + " def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))" + ], [ "LOAD_NAME", "property" @@ -8307,6 +10283,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n return self[0]" + ], [ "LOAD_NAME", "property" @@ -8315,6 +10295,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def week(self):\n return self[1]" + ], [ "LOAD_NAME", "property" @@ -8323,6 +10307,18 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def weekday(self):\n return self[2]" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_GLOBAL", "super" @@ -8431,6 +10427,34 @@ "BINARY_SUBSCR", "" ], + [ + "BUILD_STRING", + "f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})'" + ], + [ + "LOAD_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" + ], [ "LOAD_NAME", "property" @@ -8439,6 +10463,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -8447,6 +10475,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -8455,6 +10487,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -8463,6 +10499,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -8471,6 +10511,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -8479,10 +10523,58 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s" + ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "classmethod" @@ -8491,6 +10583,50 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -8579,6 +10715,10 @@ "CALL_METHOD", "hour.encode('latin1')" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -8607,6 +10747,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -8667,6 +10811,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -8695,6 +10859,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "hour" @@ -9051,6 +11219,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -9059,6 +11231,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -9071,6 +11255,10 @@ "COMPARE_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -9083,6 +11271,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -9095,6 +11287,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -9107,6 +11303,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -9255,6 +11455,10 @@ "BINARY_SUBTRACT", "self._hour * 60 + self._minute - myoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "myhhmm" + ], [ "LOAD_FAST", "other" @@ -9299,6 +11503,10 @@ "BINARY_SUBTRACT", "other._hour * 60 + other._minute - otoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "othhmm" + ], [ "LOAD_GLOBAL", "_cmp" @@ -9379,10 +11587,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -9395,6 +11611,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -9479,6 +11699,14 @@ "CALL_FUNCTION", "divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "m" @@ -9495,6 +11723,10 @@ "BINARY_MODULO", "m % timedelta(minutes=1)" ], + [ + "LOAD_FAST", + "m //= timedelta(minutes=1)" + ], [ "LOAD_GLOBAL", "timedelta" @@ -9503,6 +11735,10 @@ "CALL_FUNCTION_KW", "timedelta(minutes=1)" ], + [ + "STORE_FAST", + "m //= timedelta(minutes=1)" + ], [ "LOAD_FAST", "h" @@ -9615,6 +11851,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -9659,6 +11899,10 @@ "BINARY_MODULO", "\", %d, %d\" % (self._second, self._microsecond)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9683,6 +11927,14 @@ "BINARY_MODULO", "\", %d\" % self._second" ], + [ + "STORE_FAST", + "s" + ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9731,6 +11983,10 @@ "BINARY_MODULO", "\"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9783,6 +12039,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9815,6 +12075,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -9863,6 +12127,10 @@ "CALL_FUNCTION", "_format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9875,14 +12143,26 @@ "CALL_METHOD", "self._tzstr()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s += tz" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s += tz" + ], [ "LOAD_FAST", "s" @@ -9923,6 +12203,10 @@ "CALL_METHOD", "time_string.removeprefix('T')" ], + [ + "STORE_FAST", + "time_string" + ], [ "LOAD_FAST", "cls" @@ -9955,6 +12239,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {time_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {time_string!r}')" @@ -9983,6 +12271,10 @@ "LOAD_ATTR", "self._second" ], + [ + "STORE_FAST", + "timetuple" + ], [ "LOAD_GLOBAL", "_wrap_strftime" @@ -10119,6 +12411,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10163,6 +12459,10 @@ "CALL_METHOD", "self._tzinfo.tzname(None)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -10207,6 +12507,10 @@ "CALL_METHOD", "self._tzinfo.dst(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10239,6 +12543,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -10255,6 +12563,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -10271,6 +12583,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -10287,6 +12603,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -10303,6 +12623,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -10319,6 +12643,10 @@ "LOAD_ATTR", "self._fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -10372,8 +12700,16 @@ "self._microsecond" ], [ - "CALL_FUNCTION", - "divmod(self._microsecond, 256)" + "CALL_FUNCTION", + "divmod(self._microsecond, 256)" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" ], [ "LOAD_GLOBAL", @@ -10387,6 +12723,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -10395,6 +12739,10 @@ "LOAD_ATTR", "self._hour" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10411,6 +12759,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "h += 128" + ], + [ + "STORE_FAST", + "h += 128" + ], [ "LOAD_GLOBAL", "bytes" @@ -10451,6 +12807,10 @@ "CALL_FUNCTION", "bytes([h, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -10515,6 +12875,10 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10531,6 +12895,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "h" @@ -10667,6 +13043,22 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "LOAD_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"" + ], [ "LOAD_NAME", "date" @@ -10687,6 +13079,14 @@ "BINARY_ADD", "date.__slots__ + time.__slots__" ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" + ], [ "LOAD_NAME", "property" @@ -10695,6 +13095,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -10703,6 +13107,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -10711,6 +13119,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -10719,6 +13131,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -10727,6 +13143,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -10735,6 +13155,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], [ "LOAD_NAME", "classmethod" @@ -10743,6 +13167,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -10751,6 +13179,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10759,6 +13191,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)" + ], [ "LOAD_NAME", "classmethod" @@ -10767,6 +13203,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10775,6 +13215,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -10783,6 +13227,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)" + ], [ "LOAD_NAME", "classmethod" @@ -10791,6 +13239,66 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)" + ], + [ + "STORE_NAME", + " def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)" + ], + [ + "STORE_NAME", + " def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()" + ], + [ + "STORE_NAME", + " def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)" + ], + [ + "STORE_NAME", + " def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)" + ], + [ + "STORE_NAME", + " def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)" + ], + [ + "STORE_NAME", + " def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)" + ], + [ + "STORE_NAME", + " def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')" + ], [ "LOAD_NAME", "classmethod" @@ -10799,10 +13307,82 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff" + ], + [ + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -10887,6 +13467,10 @@ "CALL_FUNCTION", "bytes(year, 'latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -10915,6 +13499,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -10967,6 +13555,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "_check_time_fields" @@ -10995,6 +13595,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -11023,6 +13643,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -11207,6 +13831,14 @@ "CALL_METHOD", "_math.modf(t)" ], + [ + "STORE_FAST", + "frac" + ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_GLOBAL", "round" @@ -11223,6 +13855,10 @@ "CALL_FUNCTION", "round(frac * 1e6)" ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "us" @@ -11231,6 +13867,22 @@ "COMPARE_OP", "us >= 1000000" ], + [ + "LOAD_FAST", + "t += 1" + ], + [ + "STORE_FAST", + "t += 1" + ], + [ + "LOAD_FAST", + "us -= 1000000" + ], + [ + "STORE_FAST", + "us -= 1000000" + ], [ "LOAD_FAST", "us" @@ -11239,6 +13891,22 @@ "COMPARE_OP", "us < 0" ], + [ + "LOAD_FAST", + "t -= 1" + ], + [ + "STORE_FAST", + "t -= 1" + ], + [ + "LOAD_FAST", + "us += 1000000" + ], + [ + "STORE_FAST", + "us += 1000000" + ], [ "LOAD_FAST", "utc" @@ -11259,6 +13927,10 @@ "LOAD_ATTR", "_time.localtime" ], + [ + "STORE_FAST", + "converter" + ], [ "LOAD_FAST", "converter" @@ -11271,6 +13943,42 @@ "CALL_FUNCTION", "converter(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "min" @@ -11283,6 +13991,10 @@ "CALL_FUNCTION", "min(ss, 59)" ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11323,6 +14035,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "tz" @@ -11335,6 +14051,10 @@ "LOAD_FAST", "utc" ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "t" @@ -11376,20 +14096,44 @@ "t" ], [ - "LOAD_FAST", - "max_fold_seconds" + "LOAD_FAST", + "max_fold_seconds" + ], + [ + "BINARY_SUBTRACT", + "t - max_fold_seconds" + ], + [ + "CALL_FUNCTION", + "converter(t - max_fold_seconds)" + ], + [ + "BINARY_SUBSCR", + "converter(t - max_fold_seconds)[:6]" + ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" ], [ - "BINARY_SUBTRACT", - "t - max_fold_seconds" + "STORE_FAST", + "hh" ], [ - "CALL_FUNCTION", - "converter(t - max_fold_seconds)" + "STORE_FAST", + "mm" ], [ - "BINARY_SUBSCR", - "converter(t - max_fold_seconds)[:6]" + "STORE_FAST", + "ss" ], [ "LOAD_FAST", @@ -11431,6 +14175,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe1" + ], [ "LOAD_FAST", "result" @@ -11459,6 +14207,10 @@ "BINARY_SUBTRACT", "result - probe1 - timedelta(0, max_fold_seconds)" ], + [ + "STORE_FAST", + "trans" + ], [ "LOAD_FAST", "trans" @@ -11507,6 +14259,30 @@ "BINARY_SUBSCR", "converter(t + trans // timedelta(0, 1))[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11547,6 +14323,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe2" + ], [ "LOAD_FAST", "probe2" @@ -11591,6 +14371,10 @@ "CALL_METHOD", "tz.fromutc(result)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -11663,6 +14447,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11695,6 +14483,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11775,6 +14567,10 @@ "LOAD_ATTR", "time.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "cls" @@ -11899,6 +14695,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -11915,6 +14715,10 @@ "CALL_FUNCTION", "_find_isoformat_datetime_separator(date_string)" ], + [ + "STORE_FAST", + "separator_location" + ], [ "LOAD_FAST", "date_string" @@ -11927,6 +14731,10 @@ "BINARY_SUBSCR", "date_string[0:separator_location]" ], + [ + "STORE_FAST", + "dstr" + ], [ "LOAD_FAST", "date_string" @@ -11943,6 +14751,10 @@ "BINARY_SUBSCR", "date_string[(separator_location+1):]" ], + [ + "STORE_FAST", + "tstr" + ], [ "LOAD_GLOBAL", "_parse_isoformat_date" @@ -11955,6 +14767,10 @@ "CALL_FUNCTION", "_parse_isoformat_date(dstr)" ], + [ + "STORE_FAST", + "date_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -11967,6 +14783,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" @@ -11987,6 +14807,10 @@ "CALL_FUNCTION", "_parse_isoformat_time(tstr)" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -11999,10 +14823,18 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_FAST", "cls" @@ -12035,6 +14867,10 @@ "CALL_METHOD", "self.dst()" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" @@ -12043,10 +14879,22 @@ "COMPARE_OP", "dst is None" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" ], + [ + "STORE_FAST", + "dst" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12115,6 +14963,14 @@ "CALL_FUNCTION", "datetime(1970, 1, 1)" ], + [ + "STORE_DEREF", + "epoch" + ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "self" @@ -12139,6 +14995,14 @@ "BINARY_FLOOR_DIVIDE", "(self - epoch) // timedelta(0, 1)" ], + [ + "STORE_FAST", + "t" + ], + [ + "STORE_FAST", + " def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)" + ], [ "LOAD_FAST", "local" @@ -12159,6 +15023,10 @@ "BINARY_SUBTRACT", "local(t) - t" ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_FAST", "t" @@ -12171,6 +15039,10 @@ "BINARY_SUBTRACT", "t - a" ], + [ + "STORE_FAST", + "u1" + ], [ "LOAD_FAST", "local" @@ -12183,6 +15055,10 @@ "CALL_FUNCTION", "local(u1)" ], + [ + "STORE_FAST", + "t1" + ], [ "LOAD_FAST", "t1" @@ -12227,6 +15103,10 @@ "BINARY_ADD", "u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12247,6 +15127,10 @@ "BINARY_SUBTRACT", "local(u2) - u2" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12275,6 +15159,10 @@ "BINARY_SUBTRACT", "t1 - u1" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12299,6 +15187,10 @@ "BINARY_SUBTRACT", "t - b" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12311,6 +15203,10 @@ "CALL_FUNCTION", "local(u2)" ], + [ + "STORE_FAST", + "t2" + ], [ "LOAD_FAST", "t2" @@ -12395,6 +15291,30 @@ "BINARY_SUBSCR", "_time.localtime(u)[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "datetime" @@ -12471,6 +15391,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -12523,14 +15447,26 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" ], + [ + "LOAD_FAST", + "self -= offset" + ], [ "LOAD_FAST", "offset" ], + [ + "STORE_FAST", + "self -= offset" + ], [ "LOAD_FAST", "self" @@ -12555,6 +15491,18 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "self" @@ -12579,6 +15527,18 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12763,6 +15723,10 @@ "LOAD_ATTR", "self.year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -12779,6 +15743,10 @@ "LOAD_ATTR", "self.month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -12795,6 +15763,10 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "hour" @@ -12811,6 +15783,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -12827,6 +15803,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -12843,6 +15823,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -12859,6 +15843,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -12875,6 +15863,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -12891,6 +15883,10 @@ "LOAD_ATTR", "self.fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -12967,6 +15963,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_FAST", "self" @@ -12991,6 +15991,10 @@ "BINARY_FLOOR_DIVIDE", "(self - _EPOCH) // timedelta(seconds=1)" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_GLOBAL", "_time" @@ -13007,6 +16011,10 @@ "CALL_METHOD", "_time.localtime(ts)" ], + [ + "STORE_FAST", + "localtm" + ], [ "LOAD_GLOBAL", "datetime" @@ -13023,6 +16031,10 @@ "CALL_FUNCTION_EX", "datetime(*localtm[:6])" ], + [ + "STORE_FAST", + "local" + ], [ "LOAD_FAST", "localtm" @@ -13031,6 +16043,10 @@ "LOAD_ATTR", "localtm.tm_gmtoff" ], + [ + "STORE_FAST", + "gmtoff" + ], [ "LOAD_FAST", "localtm" @@ -13039,6 +16055,10 @@ "LOAD_ATTR", "localtm.tm_zone" ], + [ + "STORE_FAST", + "zone" + ], [ "LOAD_GLOBAL", "timezone" @@ -13083,6 +16103,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_GLOBAL", "isinstance" @@ -13115,6 +16139,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13135,6 +16163,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13151,6 +16183,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "mytz" @@ -13167,6 +16203,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "myoffset" @@ -13195,6 +16235,10 @@ "CALL_METHOD", "self.replace(tzinfo=None)._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13211,6 +16255,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "tz" @@ -13251,6 +16299,10 @@ "CALL_FUNCTION_KW", "(self - myoffset).replace(tzinfo=tz)" ], + [ + "STORE_FAST", + "utc" + ], [ "LOAD_FAST", "tz" @@ -13283,6 +16335,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -13435,6 +16491,10 @@ "BINARY_ADD", "\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13447,6 +16507,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -13459,14 +16523,26 @@ "CALL_FUNCTION", "_format_offset(off)" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s += tz" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s += tz" + ], [ "LOAD_FAST", "s" @@ -13527,6 +16603,10 @@ "LOAD_ATTR", "self._microsecond" ], + [ + "STORE_FAST", + "L" + ], [ "LOAD_FAST", "L" @@ -13543,6 +16623,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "L" @@ -13559,6 +16643,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "self" @@ -13611,6 +16699,10 @@ "BINARY_MODULO", "\"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13663,6 +16755,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13695,6 +16791,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -13711,6 +16811,10 @@ "CALL_FUNCTION_KW", "self.isoformat(sep=' ')" ], + [ + "STORE_FAST", + "import _strptime" + ], [ "LOAD_FAST", "_strptime" @@ -13767,6 +16871,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -13815,6 +16923,10 @@ "CALL_METHOD", "self._tzinfo.tzname(self)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -13863,6 +16975,10 @@ "CALL_METHOD", "self._tzinfo.dst(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -14247,6 +17363,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -14255,6 +17375,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -14267,6 +17399,10 @@ "COMPARE_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -14279,6 +17415,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -14291,6 +17431,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "allow_mixed" @@ -14387,6 +17531,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -14551,6 +17699,10 @@ "BINARY_SUBTRACT", "self - other" ], + [ + "STORE_FAST", + "diff" + ], [ "LOAD_FAST", "diff" @@ -14639,10 +17791,22 @@ "CALL_FUNCTION_KW", "timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)" ], + [ + "STORE_FAST", + "delta" + ], + [ + "LOAD_FAST", + "delta += other" + ], [ "LOAD_FAST", "other" ], + [ + "STORE_FAST", + "delta += other" + ], [ "LOAD_GLOBAL", "divmod" @@ -14659,6 +17823,14 @@ "CALL_FUNCTION", "divmod(delta.seconds, 3600)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "rem" + ], [ "LOAD_GLOBAL", "divmod" @@ -14671,6 +17843,14 @@ "CALL_FUNCTION", "divmod(rem, 60)" ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "delta" @@ -14831,6 +18011,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -14843,6 +18027,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_FAST", "self" @@ -14883,6 +18071,10 @@ "BINARY_ADD", "self._second + self._minute * 60 + self._hour * 3600" ], + [ + "STORE_FAST", + "secs1" + ], [ "LOAD_FAST", "other" @@ -14923,6 +18115,10 @@ "BINARY_ADD", "other._second + other._minute * 60 + other._hour * 3600" ], + [ + "STORE_FAST", + "secs2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -14975,6 +18171,10 @@ "CALL_FUNCTION", "timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)" ], + [ + "STORE_FAST", + "base" + ], [ "LOAD_FAST", "self" @@ -15011,6 +18211,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -15023,6 +18227,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -15115,10 +18323,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -15131,6 +18347,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -15203,6 +18423,10 @@ "CALL_FUNCTION", "_ymd2ord(self.year, self.month, self.day)" ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_FAST", "self" @@ -15243,6 +18467,10 @@ "BINARY_ADD", "self.hour * 3600 + self.minute * 60 + self.second" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_GLOBAL", "hash" @@ -15315,6 +18543,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "divmod" @@ -15331,6 +18567,14 @@ "CALL_FUNCTION", "divmod(self._microsecond, 256)" ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_GLOBAL", "divmod" @@ -15343,6 +18587,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -15351,6 +18603,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15367,6 +18623,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "m += 128" + ], + [ + "STORE_FAST", + "m += 128" + ], [ "LOAD_GLOBAL", "bytes" @@ -15431,6 +18695,10 @@ "CALL_FUNCTION", "bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -15495,6 +18763,18 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15527,6 +18807,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "m" @@ -15687,6 +18979,10 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "STORE_FAST", + "THURSDAY" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -15699,6 +18995,10 @@ "CALL_FUNCTION", "_ymd2ord(year, 1, 1)" ], + [ + "STORE_FAST", + "firstday" + ], [ "LOAD_FAST", "firstday" @@ -15711,6 +19011,10 @@ "BINARY_MODULO", "(firstday + 6) % 7" ], + [ + "STORE_FAST", + "firstweekday" + ], [ "LOAD_FAST", "firstday" @@ -15723,6 +19027,10 @@ "BINARY_SUBTRACT", "firstday - firstweekday" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_FAST", "firstweekday" @@ -15735,10 +19043,34 @@ "COMPARE_OP", "firstweekday > THURSDAY" ], + [ + "LOAD_FAST", + "week1monday += 7" + ], + [ + "STORE_FAST", + "week1monday += 7" + ], [ "LOAD_FAST", "week1monday" ], + [ + "LOAD_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "__slots__" + ], [ "LOAD_NAME", "object" @@ -15747,10 +19079,18 @@ "CALL_FUNCTION", "object()" ], + [ + "STORE_NAME", + "_Omitted" + ], [ "LOAD_NAME", "_Omitted" ], + [ + "STORE_NAME", + " def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)" + ], [ "LOAD_NAME", "classmethod" @@ -15759,6 +19099,46 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self" + ], + [ + "STORE_NAME", + " def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __hash__(self):\n return hash(self._offset)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __str__(self):\n return self.tzname(None)" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")" + ], [ "LOAD_NAME", "timedelta" @@ -15767,6 +19147,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=24, microseconds=-1)" ], + [ + "STORE_NAME", + "_maxoffset" + ], [ "LOAD_NAME", "_maxoffset" @@ -15775,6 +19159,10 @@ "UNARY_NEGATIVE", "-_maxoffset" ], + [ + "STORE_NAME", + "_minoffset" + ], [ "LOAD_NAME", "staticmethod" @@ -15783,6 +19171,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15835,6 +19227,10 @@ "LOAD_ATTR", "cls.utc" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15923,6 +19319,10 @@ "CALL_METHOD", "tzinfo.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "offset" @@ -16391,6 +19791,10 @@ "COMPARE_OP", "delta < timedelta(0)" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "delta" @@ -16399,6 +19803,14 @@ "UNARY_NEGATIVE", "-delta" ], + [ + "STORE_FAST", + "delta" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -16419,6 +19831,14 @@ "CALL_FUNCTION", "divmod(delta, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hours" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -16439,6 +19859,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "minutes" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -16447,6 +19875,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "rest" @@ -16455,6 +19887,10 @@ "LOAD_ATTR", "rest.microseconds" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_FAST", "microseconds" @@ -16479,6 +19915,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}'" + ], [ "LOAD_FAST", "seconds" @@ -16499,6 +19939,10 @@ "LOAD_FAST", "" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'" + ], [ "LOAD_FAST", "" @@ -16510,5 +19954,9 @@ [ "LOAD_FAST", "" + ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}'" ] ] \ No newline at end of file diff --git a/tests/sample_results/datetime-py-3.9.json b/tests/sample_results/datetime-py-3.9.json index 57d2c7d..321d6c8 100644 --- a/tests/sample_results/datetime-py-3.9.json +++ b/tests/sample_results/datetime-py-3.9.json @@ -1,4 +1,56 @@ [ + [ + "STORE_NAME", + "\"\"\"Concrete date/time and related types.\n\nSee http://www.iana.org/time-zones/repository/tz-link.html for\ntime zone and DST data sources.\n\"\"\"" + ], + [ + "STORE_NAME", + "__all__" + ], + [ + "STORE_NAME", + "import time as _time" + ], + [ + "STORE_NAME", + "import math as _math" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from operator import index as _index" + ], + [ + "STORE_NAME", + "def _cmp(x, y):\n return 0 if x == y else 1 if x > y else -1" + ], + [ + "STORE_NAME", + "MINYEAR" + ], + [ + "STORE_NAME", + "MAXYEAR" + ], + [ + "STORE_NAME", + "_MAXORDINAL" + ], + [ + "STORE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "STORE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "STORE_NAME", + "dbm" + ], [ "LOAD_NAME", "_DAYS_IN_MONTH" @@ -7,6 +59,10 @@ "BINARY_SUBSCR", "_DAYS_IN_MONTH[1:]" ], + [ + "STORE_NAME", + "dim" + ], [ "LOAD_NAME", "_DAYS_BEFORE_MONTH" @@ -25,8 +81,44 @@ ], [ "LOAD_NAME", + "dbm" + ], + [ + "LOAD_NAME", + "dim" + ], + [ + "STORE_NAME", + "dbm" + ], + [ + "DELETE_NAME", + "dbm" + ], + [ + "DELETE_NAME", "dim" ], + [ + "STORE_NAME", + "def _is_leap(year):\n \"year -> 1 if leap year, else 0.\"\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)" + ], + [ + "STORE_NAME", + "def _days_before_year(year):\n \"year -> number of days before January 1st of year.\"\n y = year - 1\n return y*365 + y//4 - y//100 + y//400" + ], + [ + "STORE_NAME", + "def _days_in_month(year, month):\n \"year, month -> number of days in that month in that year.\"\n assert 1 <= month <= 12, month\n if month == 2 and _is_leap(year):\n return 29\n return _DAYS_IN_MONTH[month]" + ], + [ + "STORE_NAME", + "def _days_before_month(year, month):\n \"year, month -> number of days in year preceding first day of month.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))" + ], + [ + "STORE_NAME", + "def _ymd2ord(year, month, day):\n \"year, month, day -> ordinal, considering 01-Jan-0001 as day 1.\"\n assert 1 <= month <= 12, 'month must be in 1..12'\n dim = _days_in_month(year, month)\n assert 1 <= day <= dim, ('day must be in 1..%d' % dim)\n return (_days_before_year(year) +\n _days_before_month(year, month) +\n day)" + ], [ "LOAD_NAME", "_days_before_year" @@ -35,6 +127,10 @@ "CALL_FUNCTION", "_days_before_year(401)" ], + [ + "STORE_NAME", + "_DI400Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -43,6 +139,10 @@ "CALL_FUNCTION", "_days_before_year(101)" ], + [ + "STORE_NAME", + "_DI100Y" + ], [ "LOAD_NAME", "_days_before_year" @@ -51,6 +151,10 @@ "CALL_FUNCTION", "_days_before_year(5)" ], + [ + "STORE_NAME", + "_DI4Y" + ], [ "LOAD_NAME", "_DI4Y" @@ -99,6 +203,98 @@ "COMPARE_OP", "_DI100Y == 25 * _DI4Y - 1" ], + [ + "STORE_NAME", + "def _ord2ymd(n):\n \"ordinal -> (year, month, day), considering 01-Jan-0001 as day 1.\"\n\n # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years\n # repeats exactly every 400 years. The basic strategy is to find the\n # closest 400-year boundary at or before n, then work with the offset\n # from that boundary to n. Life is much clearer if we subtract 1 from\n # n first -- then the values of n at 400-year boundaries are exactly\n # those divisible by _DI400Y:\n #\n # D M Y n n-1\n # -- --- ---- ---------- ----------------\n # 31 Dec -400 -_DI400Y -_DI400Y -1\n # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary\n # ...\n # 30 Dec 000 -1 -2\n # 31 Dec 000 0 -1\n # 1 Jan 001 1 0 400-year boundary\n # 2 Jan 001 2 1\n # 3 Jan 001 3 2\n # ...\n # 31 Dec 400 _DI400Y _DI400Y -1\n # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary\n n -= 1\n n400, n = divmod(n, _DI400Y)\n year = n400 * 400 + 1 # ..., -399, 1, 401, ...\n\n # Now n is the (non-negative) offset, in days, from January 1 of year, to\n # the desired date. Now compute how many 100-year cycles precede n.\n # Note that it's possible for n100 to equal 4! In that case 4 full\n # 100-year cycles precede the desired day, which implies the desired\n # day is December 31 at the end of a 400-year cycle.\n n100, n = divmod(n, _DI100Y)\n\n # Now compute how many 4-year cycles precede it.\n n4, n = divmod(n, _DI4Y)\n\n # And now how many single years. Again n1 can be 4, and again meaning\n # that the desired day is December 31 at the end of the 4-year cycle.\n n1, n = divmod(n, 365)\n\n year += n100 * 100 + n4 * 4 + n1\n if n1 == 4 or n100 == 4:\n assert n == 0\n return year-1, 12, 31\n\n # Now the year is correct, and n is the offset from January 1. We find\n # the month via an estimate that's either exact or one too large.\n leapyear = n1 == 3 and (n4 != 24 or n100 == 3)\n assert leapyear == _is_leap(year)\n month = (n + 50) >> 5\n preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)\n if preceding > n: # estimate is too large\n month -= 1\n preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)\n n -= preceding\n assert 0 <= n < _days_in_month(year, month)\n\n # Now the year and month are correct, and n is the offset from the\n # start of that month: we're done!\n return year, month, n+1" + ], + [ + "STORE_NAME", + "_MONTHNAMES" + ], + [ + "STORE_NAME", + "_DAYNAMES" + ], + [ + "STORE_NAME", + "def _build_struct_time(y, m, d, hh, mm, ss, dstflag):\n wday = (_ymd2ord(y, m, d) + 6) % 7\n dnum = _days_before_month(y, m) + d\n return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" + ], + [ + "STORE_NAME", + "def _format_time(hh, mm, ss, us, timespec='auto'):\n specs = {\n 'hours': '{:02d}',\n 'minutes': '{:02d}:{:02d}',\n 'seconds': '{:02d}:{:02d}:{:02d}',\n 'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',\n 'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'\n }\n\n if timespec == 'auto':\n # Skip trailing microseconds when us==0.\n timespec = 'microseconds' if us else 'seconds'\n elif timespec == 'milliseconds':\n us //= 1000\n try:\n fmt = specs[timespec]\n except KeyError:\n raise ValueError('Unknown timespec value')\n else:\n return fmt.format(hh, mm, ss, us)" + ], + [ + "STORE_NAME", + "def _format_offset(off):\n s = ''\n if off is not None:\n if off.days < 0:\n sign = \"-\"\n off = -off\n else:\n sign = \"+\"\n hh, mm = divmod(off, timedelta(hours=1))\n mm, ss = divmod(mm, timedelta(minutes=1))\n s += \"%s%02d:%02d\" % (sign, hh, mm)\n if ss or ss.microseconds:\n s += \":%02d\" % ss.seconds\n\n if ss.microseconds:\n s += '.%06d' % ss.microseconds\n return s" + ], + [ + "STORE_NAME", + "def _wrap_strftime(object, format, timetuple):\n # Don't call utcoffset() or tzname() unless actually needed.\n freplace = None # the string to use for %f\n zreplace = None # the string to use for %z\n Zreplace = None # the string to use for %Z\n\n # Scan format for %z and %Z escapes, replacing as needed.\n newformat = []\n push = newformat.append\n i, n = 0, len(format)\n while i < n:\n ch = format[i]\n i += 1\n if ch == '%':\n if i < n:\n ch = format[i]\n i += 1\n if ch == 'f':\n if freplace is None:\n freplace = '%06d' % getattr(object,\n 'microsecond', 0)\n newformat.append(freplace)\n elif ch == 'z':\n if zreplace is None:\n zreplace = \"\"\n if hasattr(object, \"utcoffset\"):\n offset = object.utcoffset()\n if offset is not None:\n sign = '+'\n if offset.days < 0:\n offset = -offset\n sign = '-'\n h, rest = divmod(offset, timedelta(hours=1))\n m, rest = divmod(rest, timedelta(minutes=1))\n s = rest.seconds\n u = offset.microseconds\n if u:\n zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u)\n elif s:\n zreplace = '%c%02d%02d%02d' % (sign, h, m, s)\n else:\n zreplace = '%c%02d%02d' % (sign, h, m)\n assert '%' not in zreplace\n newformat.append(zreplace)\n elif ch == 'Z':\n if Zreplace is None:\n Zreplace = \"\"\n if hasattr(object, \"tzname\"):\n s = object.tzname()\n if s is not None:\n # strftime is going to have at this: escape %\n Zreplace = s.replace('%', '%%')\n newformat.append(Zreplace)\n else:\n push('%')\n push(ch)\n else:\n push('%')\n else:\n push(ch)\n newformat = \"\".join(newformat)\n return _time.strftime(newformat, timetuple)" + ], + [ + "STORE_NAME", + "def _is_ascii_digit(c):\n return c in \"0123456789\"" + ], + [ + "STORE_NAME", + "def _find_isoformat_datetime_separator(dtstr):\n # See the comment in _datetimemodule.c:_find_isoformat_datetime_separator\n len_dtstr = len(dtstr)\n if len_dtstr == 7:\n return 7\n\n assert len_dtstr > 7\n date_separator = \"-\"\n week_indicator = \"W\"\n\n if dtstr[4] == date_separator:\n if dtstr[5] == week_indicator:\n if len_dtstr < 8:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 8 and dtstr[8] == date_separator:\n if len_dtstr == 9:\n raise ValueError(\"Invalid ISO string\")\n if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):\n # This is as far as we need to resolve the ambiguity for\n # the moment - if we have YYYY-Www-##, the separator is\n # either a hyphen at 8 or a number at 10.\n #\n # We'll assume it's a hyphen at 8 because it's way more\n # likely that someone will use a hyphen as a separator than\n # a number, but at this point it's really best effort\n # because this is an extension of the spec anyway.\n # TODO(pganssle): Document this\n return 8\n return 10\n else:\n # YYYY-Www (8)\n return 8\n else:\n # YYYY-MM-DD (10)\n return 10\n else:\n if dtstr[4] == week_indicator:\n # YYYYWww (7) or YYYYWwwd (8)\n idx = 7\n while idx < len_dtstr:\n if not _is_ascii_digit(dtstr[idx]):\n break\n idx += 1\n\n if idx < 9:\n return idx\n\n if idx % 2 == 0:\n # If the index of the last number is even, it's YYYYWwwd\n return 7\n else:\n return 8\n else:\n # YYYYMMDD (8)\n return 8" + ], + [ + "STORE_NAME", + "def _parse_isoformat_date(dtstr):\n # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,\n # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator\n assert len(dtstr) in (7, 8, 10)\n year = int(dtstr[0:4])\n has_sep = dtstr[4] == '-'\n\n pos = 4 + has_sep\n if dtstr[pos:pos + 1] == \"W\":\n # YYYY-?Www-?D?\n pos += 1\n weekno = int(dtstr[pos:pos + 2])\n pos += 2\n\n dayno = 1\n if len(dtstr) > pos:\n if (dtstr[pos:pos + 1] == '-') != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n\n dayno = int(dtstr[pos:pos + 1])\n\n return list(_isoweek_to_gregorian(year, weekno, dayno))\n else:\n month = int(dtstr[pos:pos + 2])\n pos += 2\n if (dtstr[pos:pos + 1] == \"-\") != has_sep:\n raise ValueError(\"Inconsistent use of dash separator\")\n\n pos += has_sep\n day = int(dtstr[pos:pos + 2])\n\n return [year, month, day]" + ], + [ + "STORE_NAME", + "_FRACTION_CORRECTION" + ], + [ + "STORE_NAME", + "def _parse_hh_mm_ss_ff(tstr):\n # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]\n len_str = len(tstr)\n\n time_comps = [0, 0, 0, 0]\n pos = 0\n for comp in range(0, 3):\n if (len_str - pos) < 2:\n raise ValueError(\"Incomplete time component\")\n\n time_comps[comp] = int(tstr[pos:pos+2])\n\n pos += 2\n next_char = tstr[pos:pos+1]\n\n if comp == 0:\n has_sep = next_char == ':'\n\n if not next_char or comp >= 2:\n break\n\n if has_sep and next_char != ':':\n raise ValueError(\"Invalid time separator: %c\" % next_char)\n\n pos += has_sep\n\n if pos < len_str:\n if tstr[pos] not in '.,':\n raise ValueError(\"Invalid microsecond component\")\n else:\n pos += 1\n\n len_remainder = len_str - pos\n\n if len_remainder >= 6:\n to_parse = 6\n else:\n to_parse = len_remainder\n\n time_comps[3] = int(tstr[pos:(pos+to_parse)])\n if to_parse < 6:\n time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]\n if (len_remainder > to_parse\n and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))):\n raise ValueError(\"Non-digit values in unparsed fraction\")\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _parse_isoformat_time(tstr):\n # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]\n len_str = len(tstr)\n if len_str < 2:\n raise ValueError(\"Isoformat time too short\")\n\n # This is equivalent to re.search('[+-Z]', tstr), but faster\n tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1)\n timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr\n\n time_comps = _parse_hh_mm_ss_ff(timestr)\n\n tzi = None\n if tz_pos == len_str and tstr[-1] == 'Z':\n tzi = timezone.utc\n elif tz_pos > 0:\n tzstr = tstr[tz_pos:]\n\n # Valid time zone strings are:\n # HH len: 2\n # HHMM len: 4\n # HH:MM len: 5\n # HHMMSS len: 6\n # HHMMSS.f+ len: 7+\n # HH:MM:SS len: 8\n # HH:MM:SS.f+ len: 10+\n\n if len(tzstr) in (0, 1, 3):\n raise ValueError(\"Malformed time zone string\")\n\n tz_comps = _parse_hh_mm_ss_ff(tzstr)\n\n if all(x == 0 for x in tz_comps):\n tzi = timezone.utc\n else:\n tzsign = -1 if tstr[tz_pos - 1] == '-' else 1\n\n td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])\n\n tzi = timezone(tzsign * td)\n\n time_comps.append(tzi)\n\n return time_comps" + ], + [ + "STORE_NAME", + "def _isoweek_to_gregorian(year, week, day):\n # Year is bounded this way because 9999-12-31 is (9999, 52, 5)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError(f\"Year is out of range: {year}\")\n\n if not 0 < week < 53:\n out_of_range = True\n\n if week == 53:\n # ISO years have 53 weeks in them on years starting with a\n # Thursday and leap years starting on a Wednesday\n first_weekday = _ymd2ord(year, 1, 1) % 7\n if (first_weekday == 4 or (first_weekday == 3 and\n _is_leap(year))):\n out_of_range = False\n\n if out_of_range:\n raise ValueError(f\"Invalid week: {week}\")\n\n if not 0 < day < 8:\n raise ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")\n\n # Now compute the offset from (Y, 1, 1) in days:\n day_offset = (week - 1) * 7 + (day - 1)\n\n # Calculate the ordinal day for monday, week 1\n day_1 = _isoweek1monday(year)\n ord_day = day_1 + day_offset\n\n return _ord2ymd(ord_day)" + ], + [ + "STORE_NAME", + "def _check_tzname(name):\n if name is not None and not isinstance(name, str):\n raise TypeError(\"tzinfo.tzname() must return None or string, \"\n \"not '%s'\" % type(name))" + ], + [ + "STORE_NAME", + "def _check_utc_offset(name, offset):\n assert name in (\"utcoffset\", \"dst\")\n if offset is None:\n return\n if not isinstance(offset, timedelta):\n raise TypeError(\"tzinfo.%s() must return None \"\n \"or timedelta, not '%s'\" % (name, type(offset)))\n if not -timedelta(1) < offset < timedelta(1):\n raise ValueError(\"%s()=%s, must be strictly between \"\n \"-timedelta(hours=24) and timedelta(hours=24)\" %\n (name, offset))" + ], + [ + "STORE_NAME", + "def _check_date_fields(year, month, day):\n year = _index(year)\n month = _index(month)\n day = _index(day)\n if not MINYEAR <= year <= MAXYEAR:\n raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)\n if not 1 <= month <= 12:\n raise ValueError('month must be in 1..12', month)\n dim = _days_in_month(year, month)\n if not 1 <= day <= dim:\n raise ValueError('day must be in 1..%d' % dim, day)\n return year, month, day" + ], + [ + "STORE_NAME", + "def _check_time_fields(hour, minute, second, microsecond, fold):\n hour = _index(hour)\n minute = _index(minute)\n second = _index(second)\n microsecond = _index(microsecond)\n if not 0 <= hour <= 23:\n raise ValueError('hour must be in 0..23', hour)\n if not 0 <= minute <= 59:\n raise ValueError('minute must be in 0..59', minute)\n if not 0 <= second <= 59:\n raise ValueError('second must be in 0..59', second)\n if not 0 <= microsecond <= 999999:\n raise ValueError('microsecond must be in 0..999999', microsecond)\n if fold not in (0, 1):\n raise ValueError('fold must be either 0 or 1', fold)\n return hour, minute, second, microsecond, fold" + ], + [ + "STORE_NAME", + "def _check_tzinfo_arg(tz):\n if tz is not None and not isinstance(tz, tzinfo):\n raise TypeError(\"tzinfo argument must be None or of a tzinfo subclass\")" + ], + [ + "STORE_NAME", + "def _cmperror(x, y):\n raise TypeError(\"can't compare '%s' to '%s'\" % (\n type(x).__name__, type(y).__name__))" + ], + [ + "STORE_NAME", + "def _divide_and_round(a, b):\n \"\"\"divide a by b and round result to the nearest integer\n\n When the ratio is exactly half-way between two integers,\n the even integer is returned.\n \"\"\"\n # Based on the reference implementation for divmod_near\n # in Objects/longobject.c.\n q, r = divmod(a, b)\n # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.\n # The expression r / b > 0.5 is equivalent to 2 * r > b if b is\n # positive, 2 * r < b if b negative.\n r *= 2\n greater_than_half = r > b if b > 0 else r < b\n if greater_than_half or r == b and q % 2 == 1:\n q += 1\n\n return q" + ], + [ + "CALL_FUNCTION", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "timedelta" @@ -147,10 +343,22 @@ "STORE_ATTR", "timedelta.resolution" ], + [ + "CALL_FUNCTION", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_NAME", "date" ], + [ + "STORE_NAME", + "_date_class" + ], [ "LOAD_NAME", "date" @@ -199,22 +407,62 @@ "STORE_ATTR", "date.resolution" ], + [ + "CALL_FUNCTION", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_NAME", "tuple" ], + [ + "CALL_FUNCTION", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_NAME", "IsoCalendarDate" ], + [ + "STORE_NAME", + "_IsoCalendarDate" + ], + [ + "DELETE_NAME", + "IsoCalendarDate" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "STORE_NAME", + "_tzinfo_class" + ], + [ + "CALL_FUNCTION", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "time" ], + [ + "STORE_NAME", + "_time_class" + ], [ "LOAD_NAME", "time" @@ -267,6 +515,14 @@ "LOAD_NAME", "date" ], + [ + "CALL_FUNCTION", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_NAME", "datetime" @@ -315,10 +571,22 @@ "STORE_ATTR", "datetime.resolution" ], + [ + "STORE_NAME", + "def _isoweek1monday(year):\n # Helper to calculate the day number of the Monday starting week 1\n # XXX This could be done more efficiently\n THURSDAY = 3\n firstday = _ymd2ord(year, 1, 1)\n firstweekday = (firstday + 6) % 7 # See weekday() above\n week1monday = firstday - firstweekday\n if firstweekday > THURSDAY:\n week1monday += 7\n return week1monday" + ], [ "LOAD_NAME", "tzinfo" ], + [ + "CALL_FUNCTION", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_NAME", "timezone" @@ -339,6 +607,10 @@ "CALL_METHOD", "timezone._create(timedelta(0))" ], + [ + "STORE_NAME", + "UTC" + ], [ "LOAD_NAME", "timezone" @@ -423,10 +695,186 @@ "CALL_FUNCTION_KW", "datetime(1970, 1, 1, tzinfo=timezone.utc)" ], + [ + "STORE_NAME", + "_EPOCH" + ], [ "LOAD_NAME", "ImportError" ], + [ + "DELETE_NAME", + "_DAYNAMES" + ], + [ + "DELETE_NAME", + "_DAYS_BEFORE_MONTH" + ], + [ + "DELETE_NAME", + "_DAYS_IN_MONTH" + ], + [ + "DELETE_NAME", + "_DI100Y" + ], + [ + "DELETE_NAME", + "_DI400Y" + ], + [ + "DELETE_NAME", + "_DI4Y" + ], + [ + "DELETE_NAME", + "_EPOCH" + ], + [ + "DELETE_NAME", + "_MAXORDINAL" + ], + [ + "DELETE_NAME", + "_MONTHNAMES" + ], + [ + "DELETE_NAME", + "_build_struct_time" + ], + [ + "DELETE_NAME", + "_check_date_fields" + ], + [ + "DELETE_NAME", + "_check_time_fields" + ], + [ + "DELETE_NAME", + "_check_tzinfo_arg" + ], + [ + "DELETE_NAME", + "_check_tzname" + ], + [ + "DELETE_NAME", + "_check_utc_offset" + ], + [ + "DELETE_NAME", + "_cmp" + ], + [ + "DELETE_NAME", + "_cmperror" + ], + [ + "DELETE_NAME", + "_date_class" + ], + [ + "DELETE_NAME", + "_days_before_month" + ], + [ + "DELETE_NAME", + "_days_before_year" + ], + [ + "DELETE_NAME", + "_days_in_month" + ], + [ + "DELETE_NAME", + "_format_time" + ], + [ + "DELETE_NAME", + "_format_offset" + ], + [ + "DELETE_NAME", + "_index" + ], + [ + "DELETE_NAME", + "_is_leap" + ], + [ + "DELETE_NAME", + "_isoweek1monday" + ], + [ + "DELETE_NAME", + "_math" + ], + [ + "DELETE_NAME", + "_ord2ymd" + ], + [ + "DELETE_NAME", + "_time" + ], + [ + "DELETE_NAME", + "_time_class" + ], + [ + "DELETE_NAME", + "_tzinfo_class" + ], + [ + "DELETE_NAME", + "_wrap_strftime" + ], + [ + "DELETE_NAME", + "_ymd2ord" + ], + [ + "DELETE_NAME", + "_divide_and_round" + ], + [ + "DELETE_NAME", + "_parse_isoformat_date" + ], + [ + "DELETE_NAME", + "_parse_isoformat_time" + ], + [ + "DELETE_NAME", + "_parse_hh_mm_ss_ff" + ], + [ + "DELETE_NAME", + "_IsoCalendarDate" + ], + [ + "DELETE_NAME", + "_isoweek_to_gregorian" + ], + [ + "DELETE_NAME", + "_find_isoformat_datetime_separator" + ], + [ + "DELETE_NAME", + "_FRACTION_CORRECTION" + ], + [ + "DELETE_NAME", + "_is_ascii_digit" + ], + [ + "STORE_NAME", + "from _datetime import __doc__" + ], [ "LOAD_FAST", "x" @@ -495,6 +943,10 @@ "BINARY_SUBTRACT", "year - 1" ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_FAST", "y" @@ -639,6 +1091,10 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" @@ -695,6 +1151,14 @@ "BINARY_ADD", "_days_before_year(year) +\n _days_before_month(year, month) +\n day" ], + [ + "LOAD_FAST", + "n" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -711,6 +1175,14 @@ "CALL_FUNCTION", "divmod(n, _DI400Y)" ], + [ + "STORE_FAST", + "n400" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n400" @@ -723,6 +1195,10 @@ "BINARY_ADD", "n400 * 400 + 1" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "divmod" @@ -739,6 +1215,14 @@ "CALL_FUNCTION", "divmod(n, _DI100Y)" ], + [ + "STORE_FAST", + "n100" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -755,6 +1239,14 @@ "CALL_FUNCTION", "divmod(n, _DI4Y)" ], + [ + "STORE_FAST", + "n4" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "divmod" @@ -767,6 +1259,18 @@ "CALL_FUNCTION", "divmod(n, 365)" ], + [ + "STORE_FAST", + "n1" + ], + [ + "STORE_FAST", + "n" + ], + [ + "LOAD_FAST", + "year" + ], [ "LOAD_FAST", "n100" @@ -795,6 +1299,10 @@ "BINARY_ADD", "n100 * 100 + n4 * 4 + n1" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "n1" @@ -851,6 +1359,10 @@ "COMPARE_OP", "n100 == 3" ], + [ + "STORE_FAST", + "leapyear" + ], [ "LOAD_FAST", "leapyear" @@ -883,6 +1395,10 @@ "BINARY_RSHIFT", "(n + 50) >> 5" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_DAYS_BEFORE_MONTH" @@ -911,17 +1427,33 @@ "BINARY_ADD", "_DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding" + ], [ "LOAD_FAST", "preceding" ], [ "LOAD_FAST", - "n" - ], - [ - "COMPARE_OP", - "preceding > n" + "n" + ], + [ + "COMPARE_OP", + "preceding > n" + ], + [ + "LOAD_FAST", + "month" + ], + [ + "STORE_FAST", + "month" + ], + [ + "LOAD_FAST", + "preceding" ], [ "LOAD_GLOBAL", @@ -951,10 +1483,22 @@ "BINARY_ADD", "_DAYS_IN_MONTH[month] + (month == 2 and leapyear)" ], + [ + "STORE_FAST", + "preceding" + ], + [ + "LOAD_FAST", + "n" + ], [ "LOAD_FAST", "preceding" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -1019,6 +1563,10 @@ "BINARY_MODULO", "(_ymd2ord(y, m, d) + 6) % 7" ], + [ + "STORE_FAST", + "wday" + ], [ "LOAD_GLOBAL", "_days_before_month" @@ -1043,6 +1591,10 @@ "BINARY_ADD", "_days_before_month(y, m) + d" ], + [ + "STORE_FAST", + "dnum" + ], [ "LOAD_GLOBAL", "_time" @@ -1091,6 +1643,10 @@ "CALL_METHOD", "_time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))" ], + [ + "STORE_FAST", + "specs" + ], [ "LOAD_FAST", "timespec" @@ -1103,6 +1659,10 @@ "LOAD_FAST", "us" ], + [ + "STORE_FAST", + "timespec" + ], [ "LOAD_FAST", "timespec" @@ -1111,6 +1671,14 @@ "COMPARE_OP", "timespec == 'milliseconds'" ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "specs" @@ -1123,6 +1691,10 @@ "BINARY_SUBSCR", "specs[timespec]" ], + [ + "STORE_FAST", + "fmt" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1163,6 +1735,10 @@ "CALL_METHOD", "fmt.format(hh, mm, ss, us)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "off" @@ -1183,6 +1759,10 @@ "COMPARE_OP", "off.days < 0" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "off" @@ -1191,6 +1771,14 @@ "UNARY_NEGATIVE", "-off" ], + [ + "STORE_FAST", + "off" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1211,6 +1799,14 @@ "CALL_FUNCTION", "divmod(off, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_GLOBAL", "divmod" @@ -1231,6 +1827,18 @@ "CALL_FUNCTION", "divmod(mm, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "sign" @@ -1247,6 +1855,10 @@ "BINARY_MODULO", "\"%s%02d:%02d\" % (sign, hh, mm)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1259,6 +1871,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1271,6 +1887,10 @@ "BINARY_MODULO", "\":%02d\" % ss.seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1279,6 +1899,10 @@ "LOAD_ATTR", "ss.microseconds" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "ss" @@ -1291,10 +1915,30 @@ "BINARY_MODULO", "'.%06d' % ss.microseconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" ], + [ + "STORE_FAST", + "freplace" + ], + [ + "STORE_FAST", + "zreplace" + ], + [ + "STORE_FAST", + "Zreplace" + ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_FAST", "newformat" @@ -1303,6 +1947,10 @@ "LOAD_ATTR", "newformat.append" ], + [ + "STORE_FAST", + "push" + ], [ "LOAD_GLOBAL", "len" @@ -1315,6 +1963,14 @@ "CALL_FUNCTION", "len(format)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "i" @@ -1339,6 +1995,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "ch" @@ -1371,6 +2039,18 @@ "BINARY_SUBSCR", "format[i]" ], + [ + "STORE_FAST", + "ch" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "ch" @@ -1403,6 +2083,10 @@ "BINARY_MODULO", "'%06d' % getattr(object,\n 'microsecond', 0)" ], + [ + "STORE_FAST", + "freplace" + ], [ "LOAD_FAST", "newformat" @@ -1435,6 +2119,10 @@ "IS_OP", "zreplace is None" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1459,6 +2147,10 @@ "CALL_METHOD", "object.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" @@ -1467,6 +2159,10 @@ "IS_OP", "offset is not None" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "offset" @@ -1487,6 +2183,14 @@ "UNARY_NEGATIVE", "-offset" ], + [ + "STORE_FAST", + "offset" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -1507,6 +2211,14 @@ "CALL_FUNCTION", "divmod(offset, timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -1527,6 +2239,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -1535,6 +2255,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "offset" @@ -1543,6 +2267,10 @@ "LOAD_ATTR", "offset.microseconds" ], + [ + "STORE_FAST", + "u" + ], [ "LOAD_FAST", "u" @@ -1571,6 +2299,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d.%06d' % (sign, h, m, s, u)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "s" @@ -1595,6 +2327,10 @@ "BINARY_MODULO", "'%c%02d%02d%02d' % (sign, h, m, s)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "sign" @@ -1611,6 +2347,10 @@ "BINARY_MODULO", "'%c%02d%02d' % (sign, h, m)" ], + [ + "STORE_FAST", + "zreplace" + ], [ "LOAD_FAST", "zreplace" @@ -1651,6 +2391,10 @@ "IS_OP", "Zreplace is None" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_GLOBAL", "hasattr" @@ -1675,6 +2419,10 @@ "CALL_METHOD", "object.tzname()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -1695,6 +2443,10 @@ "CALL_METHOD", "s.replace('%', '%%')" ], + [ + "STORE_FAST", + "Zreplace" + ], [ "LOAD_FAST", "newformat" @@ -1763,6 +2515,10 @@ "CALL_METHOD", "\"\".join(newformat)" ], + [ + "STORE_FAST", + "newformat" + ], [ "LOAD_GLOBAL", "_time" @@ -1803,6 +2559,10 @@ "CALL_FUNCTION", "len(dtstr)" ], + [ + "STORE_FAST", + "len_dtstr" + ], [ "LOAD_FAST", "len_dtstr" @@ -1819,6 +2579,14 @@ "COMPARE_OP", "len_dtstr > 7" ], + [ + "STORE_FAST", + "date_separator" + ], + [ + "STORE_FAST", + "week_indicator" + ], [ "LOAD_FAST", "dtstr" @@ -1947,6 +2715,10 @@ "COMPARE_OP", "dtstr[4] == week_indicator" ], + [ + "STORE_FAST", + "idx" + ], [ "LOAD_FAST", "idx" @@ -1983,6 +2755,14 @@ "LOAD_FAST", "idx" ], + [ + "STORE_FAST", + "idx" + ], + [ + "LOAD_FAST", + "idx" + ], [ "COMPARE_OP", "idx < 9" @@ -2035,6 +2815,10 @@ "CALL_FUNCTION", "int(dtstr[0:4])" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "dtstr" @@ -2047,6 +2831,10 @@ "COMPARE_OP", "dtstr[4] == '-'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "has_sep" @@ -2055,6 +2843,10 @@ "BINARY_ADD", "4 + has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "dtstr" @@ -2079,6 +2871,14 @@ "COMPARE_OP", "dtstr[pos:pos + 1] == \"W\"" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2107,6 +2907,22 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "weekno" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "len" @@ -2167,10 +2983,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2199,6 +3023,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 1])" ], + [ + "STORE_FAST", + "dayno" + ], [ "LOAD_GLOBAL", "list" @@ -2255,6 +3083,18 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "month" + ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "dtstr" @@ -2295,10 +3135,18 @@ "CALL_FUNCTION", "ValueError(\"Inconsistent use of dash separator\")" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "int" @@ -2327,6 +3175,10 @@ "CALL_FUNCTION", "int(dtstr[pos:pos + 2])" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "year" @@ -2351,6 +3203,18 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_GLOBAL", "range" @@ -2359,6 +3223,10 @@ "CALL_FUNCTION", "range(0, 3)" ], + [ + "STORE_FAST", + "comp" + ], [ "LOAD_FAST", "len_str" @@ -2423,6 +3291,14 @@ "STORE_SUBSCR", "time_comps[comp]" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "tstr" @@ -2443,6 +3319,10 @@ "BINARY_SUBSCR", "tstr[pos:pos+1]" ], + [ + "STORE_FAST", + "next_char" + ], [ "LOAD_FAST", "comp" @@ -2459,6 +3339,10 @@ "COMPARE_OP", "next_char == ':'" ], + [ + "STORE_FAST", + "has_sep" + ], [ "LOAD_FAST", "next_char" @@ -2499,10 +3383,18 @@ "CALL_FUNCTION", "ValueError(\"Invalid time separator: %c\" % next_char)" ], + [ + "LOAD_FAST", + "pos" + ], [ "LOAD_FAST", "has_sep" ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "pos" @@ -2539,6 +3431,14 @@ "CALL_FUNCTION", "ValueError(\"Invalid microsecond component\")" ], + [ + "LOAD_FAST", + "pos" + ], + [ + "STORE_FAST", + "pos" + ], [ "LOAD_FAST", "len_str" @@ -2551,6 +3451,10 @@ "BINARY_SUBTRACT", "len_str - pos" ], + [ + "STORE_FAST", + "len_remainder" + ], [ "LOAD_FAST", "len_remainder" @@ -2559,10 +3463,18 @@ "COMPARE_OP", "len_remainder >= 6" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_FAST", "len_remainder" ], + [ + "STORE_FAST", + "to_parse" + ], [ "LOAD_GLOBAL", "int" @@ -2615,6 +3527,10 @@ "LOAD_FAST", "time_comps" ], + [ + "BINARY_SUBSCR", + "time_comps[3]" + ], [ "LOAD_GLOBAL", "_FRACTION_CORRECTION" @@ -2711,6 +3627,10 @@ "CALL_FUNCTION", "len(tstr)" ], + [ + "STORE_FAST", + "len_str" + ], [ "LOAD_FAST", "len_str" @@ -2775,6 +3695,10 @@ "BINARY_ADD", "tstr.find('Z') + 1" ], + [ + "STORE_FAST", + "tz_pos" + ], [ "LOAD_FAST", "tz_pos" @@ -2803,6 +3727,10 @@ "LOAD_FAST", "tstr" ], + [ + "STORE_FAST", + "timestr" + ], [ "LOAD_GLOBAL", "_parse_hh_mm_ss_ff" @@ -2815,6 +3743,14 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(timestr)" ], + [ + "STORE_FAST", + "time_comps" + ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2847,6 +3783,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tz_pos" @@ -2867,6 +3807,10 @@ "BINARY_SUBSCR", "tstr[tz_pos:]" ], + [ + "STORE_FAST", + "tzstr" + ], [ "LOAD_GLOBAL", "len" @@ -2903,6 +3847,10 @@ "CALL_FUNCTION", "_parse_hh_mm_ss_ff(tzstr)" ], + [ + "STORE_FAST", + "tz_comps" + ], [ "LOAD_GLOBAL", "all" @@ -2911,6 +3859,10 @@ "LOAD_FAST", "tz_comps" ], + [ + "CALL_FUNCTION", + "(x == 0 for x in tz_comps)" + ], [ "CALL_FUNCTION", "all(x == 0 for x in tz_comps)" @@ -2923,6 +3875,10 @@ "LOAD_ATTR", "timezone.utc" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "tstr" @@ -2943,6 +3899,10 @@ "COMPARE_OP", "tstr[tz_pos - 1] == '-'" ], + [ + "STORE_FAST", + "tzsign" + ], [ "LOAD_GLOBAL", "timedelta" @@ -2983,6 +3943,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=tz_comps[0], minutes=tz_comps[1],\n seconds=tz_comps[2], microseconds=tz_comps[3])" ], + [ + "STORE_FAST", + "td" + ], [ "LOAD_GLOBAL", "timezone" @@ -3003,6 +3967,10 @@ "CALL_FUNCTION", "timezone(tzsign * td)" ], + [ + "STORE_FAST", + "tzi" + ], [ "LOAD_FAST", "time_comps" @@ -3023,6 +3991,14 @@ "LOAD_FAST", "time_comps" ], + [ + "LOAD_FAST", + "(x == 0 for x in tz_comps)" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_FAST", "x" @@ -3051,6 +4027,10 @@ "LOAD_FAST", "year" ], + [ + "BUILD_STRING", + "f\"Year is out of range: {year}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Year is out of range: {year}\")" @@ -3059,6 +4039,10 @@ "LOAD_FAST", "week" ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "week" @@ -3083,6 +4067,10 @@ "BINARY_MODULO", "_ymd2ord(year, 1, 1) % 7" ], + [ + "STORE_FAST", + "first_weekday" + ], [ "LOAD_FAST", "first_weekday" @@ -3111,6 +4099,10 @@ "CALL_FUNCTION", "_is_leap(year)" ], + [ + "STORE_FAST", + "out_of_range" + ], [ "LOAD_FAST", "out_of_range" @@ -3123,6 +4115,10 @@ "LOAD_FAST", "week" ], + [ + "BUILD_STRING", + "f\"Invalid week: {week}\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid week: {week}\")" @@ -3139,6 +4135,10 @@ "LOAD_FAST", "day" ], + [ + "BUILD_STRING", + "f\"Invalid weekday: {day} (range is [1, 7])\"" + ], [ "CALL_FUNCTION", "ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")" @@ -3167,6 +4167,10 @@ "BINARY_ADD", "(week - 1) * 7 + (day - 1)" ], + [ + "STORE_FAST", + "day_offset" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -3179,6 +4183,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "day_1" + ], [ "LOAD_FAST", "day_1" @@ -3191,6 +4199,10 @@ "BINARY_ADD", "day_1 + day_offset" ], + [ + "STORE_FAST", + "ord_day" + ], [ "LOAD_GLOBAL", "_ord2ymd" @@ -3367,6 +4379,10 @@ "CALL_FUNCTION", "_index(year)" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_index" @@ -3379,6 +4395,10 @@ "CALL_FUNCTION", "_index(month)" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_GLOBAL", "_index" @@ -3391,6 +4411,10 @@ "CALL_FUNCTION", "_index(day)" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "MINYEAR" @@ -3459,6 +4483,10 @@ "CALL_FUNCTION", "_days_in_month(year, month)" ], + [ + "STORE_FAST", + "dim" + ], [ "LOAD_FAST", "day" @@ -3511,6 +4539,10 @@ "CALL_FUNCTION", "_index(hour)" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "_index" @@ -3523,6 +4555,10 @@ "CALL_FUNCTION", "_index(minute)" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_GLOBAL", "_index" @@ -3535,6 +4571,10 @@ "CALL_FUNCTION", "_index(second)" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_GLOBAL", "_index" @@ -3547,6 +4587,10 @@ "CALL_FUNCTION", "_index(microsecond)" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "hour" @@ -3743,6 +4787,22 @@ "CALL_FUNCTION", "divmod(a, b)" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], + [ + "LOAD_FAST", + "r" + ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_FAST", "b" @@ -3775,6 +4835,10 @@ "COMPARE_OP", "r < b" ], + [ + "STORE_FAST", + "greater_than_half" + ], [ "LOAD_FAST", "greater_than_half" @@ -3807,6 +4871,50 @@ "LOAD_FAST", "q" ], + [ + "STORE_FAST", + "q" + ], + [ + "LOAD_FAST", + "q" + ], + [ + "LOAD_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class timedelta:\n \"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"\n __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self\n\n def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))\n\n def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s\n\n def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6\n\n # Read-only field accessors\n @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days\n\n @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds\n\n @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds\n\n def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented\n\n def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented\n\n def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)\n\n def __pos__(self):\n return self\n\n def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self\n\n def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented\n\n __rmul__ = __mul__\n\n def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)\n\n def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)\n\n def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))\n\n def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented\n\n def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented\n\n # Comparisons of timedelta objects with other.\n\n def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())\n\n def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)\n\n # Pickle support.\n\n def _getstate(self):\n return (self._days, self._seconds, self._microseconds)\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Represent the difference between two datetime objects.\n\n Supported operators:\n\n - add, subtract timedelta\n - unary plus, minus, abs\n - compare to timedelta\n - multiply, divide by int\n\n In addition, datetime supports subtraction of two datetime objects\n returning a timedelta, and addition or subtraction of a datetime\n and a timedelta giving a datetime.\n\n Representation: (days, seconds, microseconds). Why? Because I\n felt like it.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, days=0, seconds=0, microseconds=0,\n milliseconds=0, minutes=0, hours=0, weeks=0):\n # Doing this efficiently and accurately in C is going to be difficult\n # and error-prone, due to ubiquitous overflow possibilities, and that\n # C double doesn't have enough bits of precision to represent\n # microseconds over 10K years faithfully. The code here tries to make\n # explicit where go-fast assumptions can be relied on, in order to\n # guide the C implementation; it's way more convoluted than speed-\n # ignoring auto-overflow-to-long idiomatic Python could be.\n\n # XXX Check that all inputs are ints or floats.\n\n # Final values, all integer.\n # s and us fit in 32-bit signed ints; d isn't bounded.\n d = s = us = 0\n\n # Normalize everything to days, seconds, microseconds.\n days += weeks*7\n seconds += minutes*60 + hours*3600\n microseconds += milliseconds*1000\n\n # Get rid of all fractions, and normalize s and us.\n # Take a deep breath .\n if isinstance(days, float):\n dayfrac, days = _math.modf(days)\n daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))\n assert daysecondswhole == int(daysecondswhole) # can't overflow\n s = int(daysecondswhole)\n assert days == int(days)\n d = int(days)\n else:\n daysecondsfrac = 0.0\n d = days\n assert isinstance(daysecondsfrac, float)\n assert abs(daysecondsfrac) <= 1.0\n assert isinstance(d, int)\n assert abs(s) <= 24 * 3600\n # days isn't referenced again before redefinition\n\n if isinstance(seconds, float):\n secondsfrac, seconds = _math.modf(seconds)\n assert seconds == int(seconds)\n seconds = int(seconds)\n secondsfrac += daysecondsfrac\n assert abs(secondsfrac) <= 2.0\n else:\n secondsfrac = daysecondsfrac\n # daysecondsfrac isn't referenced again\n assert isinstance(secondsfrac, float)\n assert abs(secondsfrac) <= 2.0\n\n assert isinstance(seconds, int)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += int(seconds) # can't overflow\n assert isinstance(s, int)\n assert abs(s) <= 2 * 24 * 3600\n # seconds isn't referenced again before redefinition\n\n usdouble = secondsfrac * 1e6\n assert abs(usdouble) < 2.1e6 # exact value not critical\n # secondsfrac isn't referenced again\n\n if isinstance(microseconds, float):\n microseconds = round(microseconds + usdouble)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n else:\n microseconds = int(microseconds)\n seconds, microseconds = divmod(microseconds, 1000000)\n days, seconds = divmod(seconds, 24*3600)\n d += days\n s += seconds\n microseconds = round(microseconds + usdouble)\n assert isinstance(s, int)\n assert isinstance(microseconds, int)\n assert abs(s) <= 3 * 24 * 3600\n assert abs(microseconds) < 3.1e6\n\n # Just a little bit of carrying possible for microseconds and seconds.\n seconds, us = divmod(microseconds, 1000000)\n s += seconds\n days, s = divmod(s, 24*3600)\n d += days\n\n assert isinstance(d, int)\n assert isinstance(s, int) and 0 <= s < 24*3600\n assert isinstance(us, int) and 0 <= us < 1000000\n\n if abs(d) > 999999999:\n raise OverflowError(\"timedelta # of days is too large: %d\" % d)\n\n self = object.__new__(cls)\n self._days = d\n self._seconds = s\n self._microseconds = us\n self._hashcode = -1\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n args = []\n if self._days:\n args.append(\"days=%d\" % self._days)\n if self._seconds:\n args.append(\"seconds=%d\" % self._seconds)\n if self._microseconds:\n args.append(\"microseconds=%d\" % self._microseconds)\n if not args:\n args.append('0')\n return \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n ', '.join(args))" + ], + [ + "STORE_NAME", + " def __str__(self):\n mm, ss = divmod(self._seconds, 60)\n hh, mm = divmod(mm, 60)\n s = \"%d:%02d:%02d\" % (hh, mm, ss)\n if self._days:\n def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"\n s = (\"%d day%s, \" % plural(self._days)) + s\n if self._microseconds:\n s = s + \".%06d\" % self._microseconds\n return s" + ], + [ + "STORE_NAME", + " def total_seconds(self):\n \"\"\"Total seconds in the duration.\"\"\"\n return ((self.days * 86400 + self.seconds) * 10**6 +\n self.microseconds) / 10**6" + ], [ "LOAD_NAME", "property" @@ -3815,6 +4923,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def days(self):\n \"\"\"days\"\"\"\n return self._days" + ], [ "LOAD_NAME", "property" @@ -3823,6 +4935,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def seconds(self):\n \"\"\"seconds\"\"\"\n return self._seconds" + ], [ "LOAD_NAME", "property" @@ -3831,13 +4947,129 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microseconds(self):\n \"\"\"microseconds\"\"\"\n return self._microseconds" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days + other._days,\n self._seconds + other._seconds,\n self._microseconds + other._microseconds)\n return NotImplemented" + ], [ "LOAD_NAME", "__add__" ], [ - "LOAD_NAME", - "__mul__" + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n if isinstance(other, timedelta):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days - other._days,\n self._seconds - other._seconds,\n self._microseconds - other._microseconds)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __rsub__(self, other):\n if isinstance(other, timedelta):\n return -self + other\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __neg__(self):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(-self._days,\n -self._seconds,\n -self._microseconds)" + ], + [ + "STORE_NAME", + " def __pos__(self):\n return self" + ], + [ + "STORE_NAME", + " def __abs__(self):\n if self._days < 0:\n return -self\n else:\n return self" + ], + [ + "STORE_NAME", + " def __mul__(self, other):\n if isinstance(other, int):\n # for CPython compatibility, we cannot use\n # our __class__ here, but need a real timedelta\n return timedelta(self._days * other,\n self._seconds * other,\n self._microseconds * other)\n if isinstance(other, float):\n usec = self._to_microseconds()\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(usec * a, b))\n return NotImplemented" + ], + [ + "LOAD_NAME", + "__mul__" + ], + [ + "STORE_NAME", + "__rmul__" + ], + [ + "STORE_NAME", + " def _to_microseconds(self):\n return ((self._days * (24*3600) + self._seconds) * 1000000 +\n self._microseconds)" + ], + [ + "STORE_NAME", + " def __floordiv__(self, other):\n if not isinstance(other, (int, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec // other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, usec // other)" + ], + [ + "STORE_NAME", + " def __truediv__(self, other):\n if not isinstance(other, (int, float, timedelta)):\n return NotImplemented\n usec = self._to_microseconds()\n if isinstance(other, timedelta):\n return usec / other._to_microseconds()\n if isinstance(other, int):\n return timedelta(0, 0, _divide_and_round(usec, other))\n if isinstance(other, float):\n a, b = other.as_integer_ratio()\n return timedelta(0, 0, _divide_and_round(b * usec, a))" + ], + [ + "STORE_NAME", + " def __mod__(self, other):\n if isinstance(other, timedelta):\n r = self._to_microseconds() % other._to_microseconds()\n return timedelta(0, 0, r)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __divmod__(self, other):\n if isinstance(other, timedelta):\n q, r = divmod(self._to_microseconds(),\n other._to_microseconds())\n return q, timedelta(0, 0, r)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) == 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) < 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, timedelta):\n return self._cmp(other) > 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, timedelta)\n return _cmp(self._getstate(), other._getstate())" + ], + [ + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" + ], + [ + "STORE_NAME", + " def __bool__(self):\n return (self._days != 0 or\n self._seconds != 0 or\n self._microseconds != 0)" + ], + [ + "STORE_NAME", + " def _getstate(self):\n return (self._days, self._seconds, self._microseconds)" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "s" + ], + [ + "STORE_FAST", + "us" + ], + [ + "LOAD_FAST", + "days" ], [ "LOAD_FAST", @@ -3847,6 +5079,14 @@ "BINARY_MULTIPLY", "weeks*7" ], + [ + "STORE_FAST", + "days" + ], + [ + "LOAD_FAST", + "seconds" + ], [ "LOAD_FAST", "minutes" @@ -3867,6 +5107,14 @@ "BINARY_ADD", "minutes*60 + hours*3600" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "microseconds" + ], [ "LOAD_FAST", "milliseconds" @@ -3875,6 +5123,10 @@ "BINARY_MULTIPLY", "milliseconds*1000" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3907,6 +5159,14 @@ "CALL_METHOD", "_math.modf(days)" ], + [ + "STORE_FAST", + "dayfrac" + ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_GLOBAL", "_math" @@ -3927,6 +5187,14 @@ "CALL_METHOD", "_math.modf(dayfrac * (24.*3600.))" ], + [ + "STORE_FAST", + "daysecondsfrac" + ], + [ + "STORE_FAST", + "daysecondswhole" + ], [ "LOAD_FAST", "daysecondswhole" @@ -3959,6 +5227,10 @@ "CALL_FUNCTION", "int(daysecondswhole)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "days" @@ -3991,10 +5263,22 @@ "CALL_FUNCTION", "int(days)" ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "daysecondsfrac" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4091,6 +5375,14 @@ "CALL_METHOD", "_math.modf(seconds)" ], + [ + "STORE_FAST", + "secondsfrac" + ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "seconds" @@ -4123,10 +5415,22 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "secondsfrac" + ], [ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac" + ], [ "LOAD_GLOBAL", "abs" @@ -4147,6 +5451,10 @@ "LOAD_FAST", "daysecondsfrac" ], + [ + "STORE_FAST", + "secondsfrac" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4207,10 +5515,30 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_GLOBAL", "int" @@ -4223,6 +5551,10 @@ "CALL_FUNCTION", "int(seconds)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4263,6 +5595,10 @@ "BINARY_MULTIPLY", "secondsfrac * 1e6" ], + [ + "STORE_FAST", + "usdouble" + ], [ "LOAD_GLOBAL", "abs" @@ -4315,6 +5651,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4327,6 +5667,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4339,14 +5687,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "int" @@ -4359,6 +5731,10 @@ "CALL_FUNCTION", "int(microseconds)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4371,6 +5747,14 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "divmod" @@ -4383,14 +5767,38 @@ "CALL_FUNCTION", "divmod(seconds, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "seconds" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "round" @@ -4411,6 +5819,10 @@ "CALL_FUNCTION", "round(microseconds + usdouble)" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4487,10 +5899,26 @@ "CALL_FUNCTION", "divmod(microseconds, 1000000)" ], + [ + "STORE_FAST", + "seconds" + ], + [ + "STORE_FAST", + "us" + ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "seconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "divmod" @@ -4503,10 +5931,26 @@ "CALL_FUNCTION", "divmod(s, 24*3600)" ], + [ + "STORE_FAST", + "days" + ], + [ + "STORE_FAST", + "s" + ], + [ + "LOAD_FAST", + "d" + ], [ "LOAD_FAST", "days" ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_GLOBAL", "isinstance" @@ -4611,6 +6055,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "d" @@ -4659,6 +6107,10 @@ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -4827,6 +6279,14 @@ "CALL_FUNCTION", "divmod(self._seconds, 60)" ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "divmod" @@ -4839,6 +6299,14 @@ "CALL_FUNCTION", "divmod(mm, 60)" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], [ "LOAD_FAST", "hh" @@ -4855,6 +6323,10 @@ "BINARY_MODULO", "\"%d:%02d:%02d\" % (hh, mm, ss)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4863,6 +6335,10 @@ "LOAD_ATTR", "self._days" ], + [ + "STORE_FAST", + " def plural(n):\n return n, abs(n) != 1 and \"s\" or \"\"" + ], [ "LOAD_FAST", "plural" @@ -4891,6 +6367,10 @@ "BINARY_ADD", "(\"%d day%s, \" % plural(self._days)) + s" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -4919,6 +6399,10 @@ "BINARY_ADD", "s + \".%06d\" % self._microseconds" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -5395,6 +6879,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_FAST", "other" @@ -5407,6 +6895,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5519,6 +7015,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5631,6 +7131,10 @@ "CALL_METHOD", "self._to_microseconds()" ], + [ + "STORE_FAST", + "usec" + ], [ "LOAD_GLOBAL", "isinstance" @@ -5735,6 +7239,14 @@ "CALL_METHOD", "other.as_integer_ratio()" ], + [ + "STORE_FAST", + "a" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5811,6 +7323,10 @@ "BINARY_MODULO", "self._to_microseconds() % other._to_microseconds()" ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_GLOBAL", "timedelta" @@ -5875,6 +7391,14 @@ "CALL_FUNCTION", "divmod(self._to_microseconds(),\n other._to_microseconds())" ], + [ + "STORE_FAST", + "q" + ], + [ + "STORE_FAST", + "r" + ], [ "LOAD_FAST", "q" @@ -6271,6 +7795,42 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "class date:\n \"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"\n __slots__ = '_year', '_month', '_day', '_hashcode'\n\n def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self\n\n # Additional constructors\n\n @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)\n\n @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)\n\n @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))\n\n # Conversions to string\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)\n # XXX These shouldn't depend on time.localtime(), because that\n # clips the usable dates to [1970 .. 2038). At least ctime() is\n # easily done without using strftime() -- that's better too because\n # strftime(\"%c\", ...) is locale specific.\n\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)\n\n def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n __str__ = isoformat\n\n # Read-only field accessors\n @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year\n\n @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month\n\n @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day\n\n # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,\n # __hash__ (and helpers)\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)\n\n def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)\n\n def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)\n\n # Comparisons of date objects with other.\n\n def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented\n\n def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))\n\n def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode\n\n # Computations\n\n def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented\n\n def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7\n\n # Day-of-the-week and week-of-the-year, according to ISO\n\n def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7\n\n def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)\n\n # Pickle support.\n\n def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day]),\n\n def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo\n\n def __reduce__(self):\n return (self.__class__, self._getstate())" + ], + [ + "STORE_NAME", + "\"\"\"Concrete date type.\n\n Constructors:\n\n __new__()\n fromtimestamp()\n today()\n fromordinal()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n Methods:\n\n timetuple()\n toordinal()\n weekday()\n isoweekday(), isocalendar(), isoformat()\n ctime()\n strftime()\n\n Properties (readonly):\n year, month, day\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None):\n \"\"\"Constructor.\n\n Arguments:\n\n year, month, day (required, base 1)\n \"\"\"\n if (month is None and\n isinstance(year, (bytes, str)) and len(year) == 4 and\n 1 <= ord(year[2:3]) <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = year.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a date object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hashcode = -1\n return self" + ], + [ + "LOAD_NAME", + "classmethod" + ], + [ + "CALL_FUNCTION", + "classmethod" + ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t):\n \"Construct a date from a POSIX timestamp (like time.time()).\"\n y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6279,6 +7839,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def today(cls):\n \"Construct a date from time.time().\"\n t = _time.time()\n return cls.fromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -6287,6 +7851,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromordinal(cls, n):\n \"\"\"Construct a date from a proleptic Gregorian ordinal.\n\n January 1 of year 1 is day 1. Only the year, month and day are\n non-zero in the result.\n \"\"\"\n y, m, d = _ord2ymd(n)\n return cls(y, m, d)" + ], [ "LOAD_NAME", "classmethod" @@ -6295,6 +7863,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a date from a string in ISO 8601 format.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) not in (7, 8, 10):\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n try:\n return cls(*_parse_isoformat_date(date_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')" + ], [ "LOAD_NAME", "classmethod" @@ -6304,17 +7876,37 @@ "classmethod" ], [ - "LOAD_NAME", - "classmethod" + "STORE_NAME", + " @classmethod\n def fromisocalendar(cls, year, week, day):\n \"\"\"Construct a date from the ISO year, week number and weekday.\n\n This is the inverse of the date.isocalendar() function\"\"\"\n return cls(*_isoweek_to_gregorian(year, week, day))" ], [ - "CALL_FUNCTION", - "classmethod" + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> dt = datetime(2010, 1, 1)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0)'\n\n >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n >>> repr(dt)\n 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n \"\"\"\n return \"%s.%s(%d, %d, %d)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._year,\n self._month,\n self._day)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d 00:00:00 %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day, self._year)" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"Format using strftime().\"\n return _wrap_strftime(self, fmt, self.timetuple())" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def isoformat(self):\n \"\"\"Return the date formatted according to ISO.\n\n This is 'YYYY-MM-DD'.\n\n References:\n - http://www.w3.org/TR/NOTE-datetime\n - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n \"\"\"\n return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)" ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "property" @@ -6323,6 +7915,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n \"\"\"year (1-9999)\"\"\"\n return self._year" + ], [ "LOAD_NAME", "property" @@ -6331,6 +7927,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def month(self):\n \"\"\"month (1-12)\"\"\"\n return self._month" + ], [ "LOAD_NAME", "property" @@ -6339,10 +7939,90 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def day(self):\n \"\"\"day (1-31)\"\"\"\n return self._day" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n return _build_struct_time(self._year, self._month, self._day,\n 0, 0, 0, -1)" + ], + [ + "STORE_NAME", + " def toordinal(self):\n \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n January 1 of year 1 is day 1. Only the year, month and day values\n contribute to the result.\n \"\"\"\n return _ymd2ord(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None):\n \"\"\"Return a new date with new values for the specified fields.\"\"\"\n if year is None:\n year = self._year\n if month is None:\n month = self._month\n if day is None:\n day = self._day\n return type(self)(year, month, day)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, date):\n return self._cmp(other) == 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, date):\n return self._cmp(other) <= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) < 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, date):\n return self._cmp(other) >= 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, date):\n return self._cmp(other) > 0\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other):\n assert isinstance(other, date)\n y, m, d = self._year, self._month, self._day\n y2, m2, d2 = other._year, other._month, other._day\n return _cmp((y, m, d), (y2, m2, d2))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"Hash.\"\n if self._hashcode == -1:\n self._hashcode = hash(self._getstate())\n return self._hashcode" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a date to a timedelta.\"\n if isinstance(other, timedelta):\n o = self.toordinal() + other.days\n if 0 < o <= _MAXORDINAL:\n return type(self).fromordinal(o)\n raise OverflowError(\"result out of range\")\n return NotImplemented" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n if isinstance(other, timedelta):\n return self + timedelta(-other.days)\n if isinstance(other, date):\n days1 = self.toordinal()\n days2 = other.toordinal()\n return timedelta(days1 - days2)\n return NotImplemented" + ], + [ + "STORE_NAME", + " def weekday(self):\n \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n return (self.toordinal() + 6) % 7" + ], + [ + "STORE_NAME", + " def isoweekday(self):\n \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n # 1-Jan-0001 is a Monday\n return self.toordinal() % 7 or 7" + ], + [ + "STORE_NAME", + " def isocalendar(self):\n \"\"\"Return a named tuple containing ISO year, week number, and weekday.\n\n The first ISO week of the year is the (Mon-Sun) week\n containing the year's first Thursday; everything else derives\n from that.\n\n The first week is 1; Monday is 1 ... Sunday is 7.\n\n ISO calendar algorithm taken from\n http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n (used with permission)\n \"\"\"\n year = self._year\n week1monday = _isoweek1monday(year)\n today = _ymd2ord(self._year, self._month, self._day)\n # Internally, week and day have origin 0\n week, day = divmod(today - week1monday, 7)\n if week < 0:\n year -= 1\n week1monday = _isoweek1monday(year)\n week, day = divmod(today - week1monday, 7)\n elif week >= 52:\n if today >= _isoweek1monday(year+1):\n year += 1\n week = 0\n return _IsoCalendarDate(year, week+1, day+1)" + ], + [ + "STORE_NAME", + " def _getstate(self):\n yhi, ylo = divmod(self._year, 256)\n return bytes([yhi, ylo, self._month, self._day])," + ], + [ + "STORE_NAME", + " def __setstate(self, string):\n yhi, ylo, self._month, self._day = string\n self._year = yhi * 256 + ylo" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return (self.__class__, self._getstate())" + ], [ "LOAD_FAST", "month" @@ -6431,6 +8111,10 @@ "CALL_METHOD", "year.encode('latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -6459,6 +8143,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -6507,6 +8195,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "object" @@ -6523,6 +8223,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -6587,6 +8291,42 @@ "CALL_METHOD", "_time.localtime(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "cls" @@ -6619,6 +8359,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -6647,6 +8391,18 @@ "CALL_FUNCTION", "_ord2ymd(n)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "cls" @@ -6715,6 +8471,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6751,6 +8511,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -6851,6 +8615,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -7147,6 +8915,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -7163,6 +8935,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -7179,6 +8955,10 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "type" @@ -7447,6 +9227,18 @@ "LOAD_ATTR", "self._day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "other" @@ -7471,6 +9263,18 @@ "LOAD_ATTR", "other._day" ], + [ + "STORE_FAST", + "y2" + ], + [ + "STORE_FAST", + "m2" + ], + [ + "STORE_FAST", + "d2" + ], [ "LOAD_GLOBAL", "_cmp" @@ -7591,6 +9395,10 @@ "BINARY_ADD", "self.toordinal() + other.days" ], + [ + "STORE_FAST", + "o" + ], [ "LOAD_FAST", "o" @@ -7707,6 +9515,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -7719,6 +9531,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -7787,6 +9603,10 @@ "LOAD_ATTR", "self._year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7799,6 +9619,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -7831,6 +9655,10 @@ "CALL_FUNCTION", "_ymd2ord(self._year, self._month, self._day)" ], + [ + "STORE_FAST", + "today" + ], [ "LOAD_GLOBAL", "divmod" @@ -7851,6 +9679,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7859,6 +9695,14 @@ "COMPARE_OP", "week < 0" ], + [ + "LOAD_FAST", + "year" + ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "_isoweek1monday" @@ -7871,6 +9715,10 @@ "CALL_FUNCTION", "_isoweek1monday(year)" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_GLOBAL", "divmod" @@ -7891,6 +9739,14 @@ "CALL_FUNCTION", "divmod(today - week1monday, 7)" ], + [ + "STORE_FAST", + "week" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "week" @@ -7923,6 +9779,18 @@ "COMPARE_OP", "today >= _isoweek1monday(year+1)" ], + [ + "LOAD_FAST", + "year" + ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "week" + ], [ "LOAD_GLOBAL", "_IsoCalendarDate" @@ -7967,6 +9835,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "bytes" @@ -8003,6 +9879,14 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_FAST", "self" @@ -8063,6 +9947,46 @@ "CALL_METHOD", "self._getstate()" ], + [ + "LOAD_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "class tzinfo:\n \"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"\n __slots__ = ()\n\n def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst\n\n # Pickle support.\n\n def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], + [ + "STORE_NAME", + "\"\"\"Abstract base class for time zone info classes.\n\n Subclasses must override the name(), utcoffset() and dst() methods.\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n \"datetime -> string name of time zone.\"\n raise NotImplementedError(\"tzinfo subclass must override tzname()\")" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n Return 0 if DST not in effect. utcoffset() must include the DST\n offset.\n \"\"\"\n raise NotImplementedError(\"tzinfo subclass must override dst()\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n \"datetime in UTC -> datetime in local time.\"\n\n if not isinstance(dt, datetime):\n raise TypeError(\"fromutc() requires a datetime argument\")\n if dt.tzinfo is not self:\n raise ValueError(\"dt.tzinfo is not self\")\n\n dtoff = dt.utcoffset()\n if dtoff is None:\n raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n \"result\")\n\n # See the long comment block at the end of this file for an\n # explanation of this algorithm.\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc() requires a non-None dst() result\")\n delta = dtoff - dtdst\n if delta:\n dt += delta\n dtdst = dt.dst()\n if dtdst is None:\n raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n \"results; cannot convert\")\n return dt + dtdst" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n getinitargs = getattr(self, \"__getinitargs__\", None)\n if getinitargs:\n args = getinitargs()\n else:\n args = ()\n return (self.__class__, args, self.__getstate__())" + ], [ "LOAD_GLOBAL", "NotImplementedError" @@ -8147,6 +10071,10 @@ "CALL_METHOD", "dt.utcoffset()" ], + [ + "STORE_FAST", + "dtoff" + ], [ "LOAD_FAST", "dtoff" @@ -8175,6 +10103,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8203,14 +10135,26 @@ "BINARY_SUBTRACT", "dtoff - dtdst" ], + [ + "STORE_FAST", + "delta" + ], [ "LOAD_FAST", "delta" ], + [ + "LOAD_FAST", + "dt" + ], [ "LOAD_FAST", "delta" ], + [ + "STORE_FAST", + "dt" + ], [ "LOAD_FAST", "dt" @@ -8223,6 +10167,10 @@ "CALL_METHOD", "dt.dst()" ], + [ + "STORE_FAST", + "dtdst" + ], [ "LOAD_FAST", "dtdst" @@ -8263,6 +10211,10 @@ "CALL_FUNCTION", "getattr(self, \"__getinitargs__\", None)" ], + [ + "STORE_FAST", + "getinitargs" + ], [ "LOAD_FAST", "getinitargs" @@ -8275,6 +10227,14 @@ "CALL_FUNCTION", "getinitargs()" ], + [ + "STORE_FAST", + "args" + ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "self" @@ -8299,6 +10259,22 @@ "CALL_METHOD", "self.__getstate__()" ], + [ + "LOAD_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + "class IsoCalendarDate(tuple):\n\n def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))\n\n @property\n def year(self):\n return self[0]\n\n @property\n def week(self):\n return self[1]\n\n @property\n def weekday(self):\n return self[2]\n\n def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))\n\n def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], + [ + "STORE_NAME", + " def __new__(cls, year, week, weekday, /):\n return super().__new__(cls, (year, week, weekday))" + ], [ "LOAD_NAME", "property" @@ -8307,6 +10283,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def year(self):\n return self[0]" + ], [ "LOAD_NAME", "property" @@ -8315,6 +10295,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def week(self):\n return self[1]" + ], [ "LOAD_NAME", "property" @@ -8323,6 +10307,18 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def weekday(self):\n return self[2]" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n # This code is intended to pickle the object without making the\n # class public. See https://bugs.python.org/msg352381\n return (tuple, (tuple(self),))" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return (f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})')" + ], [ "LOAD_GLOBAL", "super" @@ -8431,6 +10427,34 @@ "BINARY_SUBSCR", "self[2]" ], + [ + "BUILD_STRING", + "f'{self.__class__.__name__}'\n f'(year={self[0]}, week={self[1]}, weekday={self[2]})'" + ], + [ + "LOAD_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class time:\n \"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"\n __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold'\n\n def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n # Standard conversions, __hash__ (and helpers)\n\n # Comparisons of time objects with other.\n\n def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented\n\n def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented\n\n def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented\n\n def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented\n\n def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))\n\n def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode\n\n # Conversion to string\n\n def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s\n\n __str__ = isoformat\n\n @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')\n\n\n def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)\n\n def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)\n\n # Timezone functions\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"Time with time zone.\n\n Constructors:\n\n __new__()\n\n Operators:\n\n __repr__, __str__\n __eq__, __le__, __lt__, __ge__, __gt__, __hash__\n\n Methods:\n\n strftime()\n isoformat()\n utcoffset()\n tzname()\n dst()\n\n Properties (readonly):\n hour, minute, second, microsecond, tzinfo, fold\n \"\"\"" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):\n \"\"\"Constructor.\n\n Arguments:\n\n hour, minute (required)\n second, microsecond (default to zero)\n tzinfo (default to None)\n fold (keyword only, default to zero)\n \"\"\"\n if (isinstance(hour, (bytes, str)) and len(hour) == 6 and\n ord(hour[0:1])&0x7F < 24):\n # Pickle support\n if isinstance(hour, str):\n try:\n hour = hour.encode('latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a time object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(hour, minute or None)\n self._hashcode = -1\n return self\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" + ], [ "LOAD_NAME", "property" @@ -8439,6 +10463,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -8447,6 +10475,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -8455,6 +10487,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -8463,6 +10499,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -8471,6 +10511,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -8479,10 +10523,58 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, time):\n return self._cmp(other, allow_mixed=True) == 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, time):\n return self._cmp(other) <= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) < 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, time):\n return self._cmp(other) >= 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, time):\n return self._cmp(other) > 0\n else:\n return NotImplemented" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, time)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._hour, self._minute, self._second,\n self._microsecond),\n (other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware times\")\n myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)\n othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)\n return _cmp((myhhmm, self._second, self._microsecond),\n (othhmm, other._second, other._microsecond))" + ], + [ + "STORE_NAME", + " def __hash__(self):\n \"\"\"Hash.\"\"\"\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if not tzoff: # zero or None\n self._hashcode = hash(t._getstate()[0])\n else:\n h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))\n assert not m % timedelta(minutes=1), \"whole minute\"\n m //= timedelta(minutes=1)\n if 0 <= h < 24:\n self._hashcode = hash(time(h, m, self.second, self.microsecond))\n else:\n self._hashcode = hash((h, m, self.second, self.microsecond))\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _tzstr(self):\n \"\"\"Return formatted timezone offset (+xx:xx) or an empty string.\"\"\"\n off = self.utcoffset()\n return _format_offset(off)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n if self._microsecond != 0:\n s = \", %d, %d\" % (self._second, self._microsecond)\n elif self._second != 0:\n s = \", %d\" % self._second\n else:\n s = \"\"\n s= \"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def isoformat(self, timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional\n part is omitted if self.microsecond == 0.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)\n tz = self._tzstr()\n if tz:\n s += tz\n return s" + ], [ "LOAD_NAME", "isoformat" ], + [ + "STORE_NAME", + "__str__" + ], [ "LOAD_NAME", "classmethod" @@ -8491,6 +10583,50 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, time_string):\n \"\"\"Construct a time from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(time_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n # The spec actually requires that time-only ISO 8601 strings start with\n # T, but the extended format allows this to be omitted as long as there\n # is no ambiguity with date strings.\n time_string = time_string.removeprefix('T')\n\n try:\n return cls(*_parse_isoformat_time(time_string))\n except Exception:\n raise ValueError(f'Invalid isoformat string: {time_string!r}')" + ], + [ + "STORE_NAME", + " def strftime(self, fmt):\n \"\"\"Format using strftime(). The date part of the timestamp passed\n to underlying strftime should not be used.\n \"\"\"\n # The year must be >= 1000 else Python's strftime implementation\n # can raise a bogus exception.\n timetuple = (1900, 1, 1,\n self._hour, self._minute, self._second,\n 0, 1, -1)\n return _wrap_strftime(self, fmt, timetuple)" + ], + [ + "STORE_NAME", + " def __format__(self, fmt):\n if not isinstance(fmt, str):\n raise TypeError(\"must be str, not %s\" % type(fmt).__name__)\n if len(fmt) != 0:\n return self.strftime(fmt)\n return str(self)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta, positive east of UTC\n (negative west of UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(None)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(None)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(None)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def replace(self, hour=None, minute=None, second=None, microsecond=None,\n tzinfo=True, *, fold=None):\n \"\"\"Return a new time with new values for the specified fields.\"\"\"\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self._fold\n return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n h = self._hour\n if self._fold and protocol > 3:\n h += 128\n basestate = bytes([h, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n h, self._minute, self._second, us1, us2, us3 = string\n if h > 127:\n self._fold = 1\n self._hour = h - 128\n else:\n self._fold = 0\n self._hour = h\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -8579,6 +10715,10 @@ "CALL_METHOD", "hour.encode('latin1')" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -8607,6 +10747,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -8667,6 +10811,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -8695,6 +10859,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "hour" @@ -9051,6 +11219,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -9059,6 +11231,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -9071,6 +11255,10 @@ "IS_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -9083,6 +11271,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -9095,6 +11287,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -9107,6 +11303,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -9255,6 +11455,10 @@ "BINARY_SUBTRACT", "self._hour * 60 + self._minute - myoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "myhhmm" + ], [ "LOAD_FAST", "other" @@ -9299,6 +11503,10 @@ "BINARY_SUBTRACT", "other._hour * 60 + other._minute - otoff//timedelta(minutes=1)" ], + [ + "STORE_FAST", + "othhmm" + ], [ "LOAD_GLOBAL", "_cmp" @@ -9379,10 +11587,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -9395,6 +11611,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -9479,6 +11699,14 @@ "CALL_FUNCTION", "divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,\n timedelta(hours=1))" ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "m" @@ -9495,6 +11723,10 @@ "BINARY_MODULO", "m % timedelta(minutes=1)" ], + [ + "LOAD_FAST", + "m" + ], [ "LOAD_GLOBAL", "timedelta" @@ -9503,6 +11735,10 @@ "CALL_FUNCTION_KW", "timedelta(minutes=1)" ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "h" @@ -9615,6 +11851,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -9659,6 +11899,10 @@ "BINARY_MODULO", "\", %d, %d\" % (self._second, self._microsecond)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9683,6 +11927,14 @@ "BINARY_MODULO", "\", %d\" % self._second" ], + [ + "STORE_FAST", + "s" + ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9731,6 +11983,10 @@ "BINARY_MODULO", "\"%s.%s(%d, %d%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._hour, self._minute, s)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9783,6 +12039,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9815,6 +12075,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -9863,6 +12127,10 @@ "CALL_FUNCTION", "_format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -9875,14 +12143,26 @@ "CALL_METHOD", "self._tzstr()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -9923,6 +12203,10 @@ "CALL_METHOD", "time_string.removeprefix('T')" ], + [ + "STORE_FAST", + "time_string" + ], [ "LOAD_FAST", "cls" @@ -9955,6 +12239,10 @@ "LOAD_FAST", "time_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {time_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {time_string!r}')" @@ -9983,6 +12271,10 @@ "LOAD_ATTR", "self._second" ], + [ + "STORE_FAST", + "timetuple" + ], [ "LOAD_GLOBAL", "_wrap_strftime" @@ -10119,6 +12411,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10163,6 +12459,10 @@ "CALL_METHOD", "self._tzinfo.tzname(None)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -10207,6 +12507,10 @@ "CALL_METHOD", "self._tzinfo.dst(None)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -10239,6 +12543,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -10255,6 +12563,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -10271,6 +12583,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -10287,6 +12603,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -10303,6 +12623,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -10319,6 +12643,10 @@ "LOAD_ATTR", "self._fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -10375,6 +12703,14 @@ "CALL_FUNCTION", "divmod(self._microsecond, 256)" ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_GLOBAL", "divmod" @@ -10387,6 +12723,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -10395,6 +12739,10 @@ "LOAD_ATTR", "self._hour" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10411,6 +12759,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "h" + ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_GLOBAL", "bytes" @@ -10451,6 +12807,10 @@ "CALL_FUNCTION", "bytes([h, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -10515,6 +12875,10 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "h" + ], [ "LOAD_FAST", "self" @@ -10531,6 +12895,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "h" @@ -10667,6 +13043,22 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "LOAD_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "class datetime(date):\n \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"\n __slots__ = date.__slots__ + time.__slots__\n\n def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self\n\n # Read-only field accessors\n @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour\n\n @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute\n\n @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second\n\n @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond\n\n @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo\n\n @property\n def fold(self):\n return self._fold\n\n @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result\n\n @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)\n\n @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)\n\n @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)\n\n @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)\n\n @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)\n\n @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))\n\n def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)\n\n def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)\n\n\n def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()\n\n def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)\n\n def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)\n\n def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)\n\n def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)\n\n def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)\n\n def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)\n\n # Ways to produce a string.\n\n def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)\n\n def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s\n\n def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')\n\n @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)\n\n def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset\n\n def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name\n\n def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset\n\n # Comparisons of datetime objects with other.\n\n def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False\n\n def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)\n\n def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0\n\n def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")\n\n __radd__ = __add__\n\n def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff\n\n def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode\n\n # Pickle support.\n\n def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)\n\n def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo\n\n def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))\n\n def __reduce__(self):\n return self.__reduce_ex__(2)" + ], + [ + "STORE_NAME", + "\"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n The year, month and day arguments are required. tzinfo may be None, or an\n instance of a tzinfo subclass. The remaining arguments may be ints.\n \"\"\"" + ], [ "LOAD_NAME", "date" @@ -10687,6 +13079,14 @@ "BINARY_ADD", "date.__slots__ + time.__slots__" ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n microsecond=0, tzinfo=None, *, fold=0):\n if (isinstance(year, (bytes, str)) and len(year) == 10 and\n 1 <= ord(year[2:3])&0x7F <= 12):\n # Pickle support\n if isinstance(year, str):\n try:\n year = bytes(year, 'latin1')\n except UnicodeEncodeError:\n # More informative error message.\n raise ValueError(\n \"Failed to encode latin1 string when unpickling \"\n \"a datetime object. \"\n \"pickle.load(data, encoding='latin1') is assumed.\")\n self = object.__new__(cls)\n self.__setstate(year, month)\n self._hashcode = -1\n return self\n year, month, day = _check_date_fields(year, month, day)\n hour, minute, second, microsecond, fold = _check_time_fields(\n hour, minute, second, microsecond, fold)\n _check_tzinfo_arg(tzinfo)\n self = object.__new__(cls)\n self._year = year\n self._month = month\n self._day = day\n self._hour = hour\n self._minute = minute\n self._second = second\n self._microsecond = microsecond\n self._tzinfo = tzinfo\n self._hashcode = -1\n self._fold = fold\n return self" + ], [ "LOAD_NAME", "property" @@ -10695,6 +13095,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def hour(self):\n \"\"\"hour (0-23)\"\"\"\n return self._hour" + ], [ "LOAD_NAME", "property" @@ -10703,6 +13107,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def minute(self):\n \"\"\"minute (0-59)\"\"\"\n return self._minute" + ], [ "LOAD_NAME", "property" @@ -10711,6 +13119,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def second(self):\n \"\"\"second (0-59)\"\"\"\n return self._second" + ], [ "LOAD_NAME", "property" @@ -10719,6 +13131,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def microsecond(self):\n \"\"\"microsecond (0-999999)\"\"\"\n return self._microsecond" + ], [ "LOAD_NAME", "property" @@ -10727,6 +13143,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def tzinfo(self):\n \"\"\"timezone info object\"\"\"\n return self._tzinfo" + ], [ "LOAD_NAME", "property" @@ -10735,6 +13155,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def fold(self):\n return self._fold" + ], [ "LOAD_NAME", "classmethod" @@ -10743,6 +13167,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _fromtimestamp(cls, t, utc, tz):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n frac, t = _math.modf(t)\n us = round(frac * 1e6)\n if us >= 1000000:\n t += 1\n us -= 1000000\n elif us < 0:\n t -= 1\n us += 1000000\n\n converter = _time.gmtime if utc else _time.localtime\n y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)\n ss = min(ss, 59) # clamp out leap seconds if the platform has them\n result = cls(y, m, d, hh, mm, ss, us, tz)\n if tz is None and not utc:\n # As of version 2015f max fold in IANA database is\n # 23 hours at 1969-09-30 13:00:00 in Kwajalein.\n # Let's probe 24 hours in the past to detect a transition:\n max_fold_seconds = 24 * 3600\n\n # On Windows localtime_s throws an OSError for negative values,\n # thus we can't perform fold detection for values of time less\n # than the max time fold. See comments in _datetimemodule's\n # version of this method for more details.\n if t < max_fold_seconds and sys.platform.startswith(\"win\"):\n return result\n\n y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]\n probe1 = cls(y, m, d, hh, mm, ss, us, tz)\n trans = result - probe1 - timedelta(0, max_fold_seconds)\n if trans.days < 0:\n y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]\n probe2 = cls(y, m, d, hh, mm, ss, us, tz)\n if probe2 == result:\n result._fold = 1\n elif tz is not None:\n result = tz.fromutc(result)\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -10751,6 +13179,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromtimestamp(cls, t, tz=None):\n \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n A timezone info object may be passed in as well.\n \"\"\"\n _check_tzinfo_arg(tz)\n\n return cls._fromtimestamp(t, tz is not None, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10759,6 +13191,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcfromtimestamp(cls, t):\n \"\"\"Construct a naive UTC datetime from a POSIX timestamp.\"\"\"\n return cls._fromtimestamp(t, True, None)" + ], [ "LOAD_NAME", "classmethod" @@ -10767,6 +13203,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def now(cls, tz=None):\n \"Construct a datetime from time.time() and optional time zone info.\"\n t = _time.time()\n return cls.fromtimestamp(t, tz)" + ], [ "LOAD_NAME", "classmethod" @@ -10775,6 +13215,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def utcnow(cls):\n \"Construct a UTC datetime from time.time().\"\n t = _time.time()\n return cls.utcfromtimestamp(t)" + ], [ "LOAD_NAME", "classmethod" @@ -10783,6 +13227,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def combine(cls, date, time, tzinfo=True):\n \"Construct a datetime from a given date and a given time.\"\n if not isinstance(date, _date_class):\n raise TypeError(\"date argument must be a date instance\")\n if not isinstance(time, _time_class):\n raise TypeError(\"time argument must be a time instance\")\n if tzinfo is True:\n tzinfo = time.tzinfo\n return cls(date.year, date.month, date.day,\n time.hour, time.minute, time.second, time.microsecond,\n tzinfo, fold=time.fold)" + ], [ "LOAD_NAME", "classmethod" @@ -10791,6 +13239,66 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def fromisoformat(cls, date_string):\n \"\"\"Construct a datetime from a string in one of the ISO 8601 formats.\"\"\"\n if not isinstance(date_string, str):\n raise TypeError('fromisoformat: argument must be str')\n\n if len(date_string) < 7:\n raise ValueError(f'Invalid isoformat string: {date_string!r}')\n\n # Split this at the separator\n try:\n separator_location = _find_isoformat_datetime_separator(date_string)\n dstr = date_string[0:separator_location]\n tstr = date_string[(separator_location+1):]\n\n date_components = _parse_isoformat_date(dstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n\n if tstr:\n try:\n time_components = _parse_isoformat_time(tstr)\n except ValueError:\n raise ValueError(\n f'Invalid isoformat string: {date_string!r}') from None\n else:\n time_components = [0, 0, 0, 0, None]\n\n return cls(*(date_components + time_components))" + ], + [ + "STORE_NAME", + " def timetuple(self):\n \"Return local time tuple compatible with time.localtime().\"\n dst = self.dst()\n if dst is None:\n dst = -1\n elif dst:\n dst = 1\n else:\n dst = 0\n return _build_struct_time(self.year, self.month, self.day,\n self.hour, self.minute, self.second,\n dst)" + ], + [ + "STORE_NAME", + " def _mktime(self):\n \"\"\"Return integer POSIX timestamp.\"\"\"\n epoch = datetime(1970, 1, 1)\n max_fold_seconds = 24 * 3600\n t = (self - epoch) // timedelta(0, 1)\n def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)\n\n # Our goal is to solve t = local(u) for u.\n a = local(t) - t\n u1 = t - a\n t1 = local(u1)\n if t1 == t:\n # We found one solution, but it may not be the one we need.\n # Look for an earlier solution (if `fold` is 0), or a\n # later one (if `fold` is 1).\n u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]\n b = local(u2) - u2\n if a == b:\n return u1\n else:\n b = t1 - u1\n assert a != b\n u2 = t - b\n t2 = local(u2)\n if t2 == t:\n return u2\n if t1 == t:\n return u1\n # We have found both offsets a and b, but neither t - a nor t - b is\n # a solution. This means t is in the gap.\n return (max, min)[self.fold](u1, u2)" + ], + [ + "STORE_NAME", + " def timestamp(self):\n \"Return POSIX timestamp as float\"\n if self._tzinfo is None:\n s = self._mktime()\n return s + self.microsecond / 1e6\n else:\n return (self - _EPOCH).total_seconds()" + ], + [ + "STORE_NAME", + " def utctimetuple(self):\n \"Return UTC time tuple compatible with time.gmtime().\"\n offset = self.utcoffset()\n if offset:\n self -= offset\n y, m, d = self.year, self.month, self.day\n hh, mm, ss = self.hour, self.minute, self.second\n return _build_struct_time(y, m, d, hh, mm, ss, 0)" + ], + [ + "STORE_NAME", + " def date(self):\n \"Return the date part.\"\n return date(self._year, self._month, self._day)" + ], + [ + "STORE_NAME", + " def time(self):\n \"Return the time part, with tzinfo None.\"\n return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold)" + ], + [ + "STORE_NAME", + " def timetz(self):\n \"Return the time part, with same tzinfo.\"\n return time(self.hour, self.minute, self.second, self.microsecond,\n self._tzinfo, fold=self.fold)" + ], + [ + "STORE_NAME", + " def replace(self, year=None, month=None, day=None, hour=None,\n minute=None, second=None, microsecond=None, tzinfo=True,\n *, fold=None):\n \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n if year is None:\n year = self.year\n if month is None:\n month = self.month\n if day is None:\n day = self.day\n if hour is None:\n hour = self.hour\n if minute is None:\n minute = self.minute\n if second is None:\n second = self.second\n if microsecond is None:\n microsecond = self.microsecond\n if tzinfo is True:\n tzinfo = self.tzinfo\n if fold is None:\n fold = self.fold\n return type(self)(year, month, day, hour, minute, second,\n microsecond, tzinfo, fold=fold)" + ], + [ + "STORE_NAME", + " def _local_timezone(self):\n if self.tzinfo is None:\n ts = self._mktime()\n else:\n ts = (self - _EPOCH) // timedelta(seconds=1)\n localtm = _time.localtime(ts)\n local = datetime(*localtm[:6])\n # Extract TZ data\n gmtoff = localtm.tm_gmtoff\n zone = localtm.tm_zone\n return timezone(timedelta(seconds=gmtoff), zone)" + ], + [ + "STORE_NAME", + " def astimezone(self, tz=None):\n if tz is None:\n tz = self._local_timezone()\n elif not isinstance(tz, tzinfo):\n raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n mytz = self.tzinfo\n if mytz is None:\n mytz = self._local_timezone()\n myoffset = mytz.utcoffset(self)\n else:\n myoffset = mytz.utcoffset(self)\n if myoffset is None:\n mytz = self.replace(tzinfo=None)._local_timezone()\n myoffset = mytz.utcoffset(self)\n\n if tz is mytz:\n return self\n\n # Convert self to UTC, and attach the new time zone object.\n utc = (self - myoffset).replace(tzinfo=tz)\n\n # Convert from UTC to tz's local time.\n return tz.fromutc(utc)" + ], + [ + "STORE_NAME", + " def ctime(self):\n \"Return ctime() style string.\"\n weekday = self.toordinal() % 7 or 7\n return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n _DAYNAMES[weekday],\n _MONTHNAMES[self._month],\n self._day,\n self._hour, self._minute, self._second,\n self._year)" + ], + [ + "STORE_NAME", + " def isoformat(self, sep='T', timespec='auto'):\n \"\"\"Return the time formatted according to ISO.\n\n The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.\n By default, the fractional part is omitted if self.microsecond == 0.\n\n If self.tzinfo is not None, the UTC offset is also attached, giving\n giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.\n\n Optional argument sep specifies the separator between date and\n time, default 'T'.\n\n The optional argument timespec specifies the number of additional\n terms of the time to include. Valid options are 'auto', 'hours',\n 'minutes', 'seconds', 'milliseconds' and 'microseconds'.\n \"\"\"\n s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec))\n\n off = self.utcoffset()\n tz = _format_offset(off)\n if tz:\n s += tz\n\n return s" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\"\"\"\n L = [self._year, self._month, self._day, # These are never zero\n self._hour, self._minute, self._second, self._microsecond]\n if L[-1] == 0:\n del L[-1]\n if L[-1] == 0:\n del L[-1]\n s = \"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))\n if self._tzinfo is not None:\n assert s[-1:] == \")\"\n s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n if self._fold:\n assert s[-1:] == \")\"\n s = s[:-1] + \", fold=1)\"\n return s" + ], + [ + "STORE_NAME", + " def __str__(self):\n \"Convert to string, for str().\"\n return self.isoformat(sep=' ')" + ], [ "LOAD_NAME", "classmethod" @@ -10799,10 +13307,82 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def strptime(cls, date_string, format):\n 'string, format -> new datetime parsed from a string (like time.strptime()).'\n import _strptime\n return _strptime._strptime_datetime(cls, date_string, format)" + ], + [ + "STORE_NAME", + " def utcoffset(self):\n \"\"\"Return the timezone offset as timedelta positive east of UTC (negative west of\n UTC).\"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.utcoffset(self)\n _check_utc_offset(\"utcoffset\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def tzname(self):\n \"\"\"Return the timezone name.\n\n Note that the name is 100% informational -- there's no requirement that\n it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n \"\"\"\n if self._tzinfo is None:\n return None\n name = self._tzinfo.tzname(self)\n _check_tzname(name)\n return name" + ], + [ + "STORE_NAME", + " def dst(self):\n \"\"\"Return 0 if DST is not in effect, or the DST offset (as timedelta\n positive eastward) if DST is in effect.\n\n This is purely informational; the DST offset has already been added to\n the UTC offset returned by utcoffset() if applicable, so there's no\n need to consult dst() unless you're interested in displaying the DST\n info.\n \"\"\"\n if self._tzinfo is None:\n return None\n offset = self._tzinfo.dst(self)\n _check_utc_offset(\"dst\", offset)\n return offset" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other, allow_mixed=True) == 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n return False" + ], + [ + "STORE_NAME", + " def __le__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) <= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) < 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __ge__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) >= 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def __gt__(self, other):\n if isinstance(other, datetime):\n return self._cmp(other) > 0\n elif not isinstance(other, date):\n return NotImplemented\n else:\n _cmperror(self, other)" + ], + [ + "STORE_NAME", + " def _cmp(self, other, allow_mixed=False):\n assert isinstance(other, datetime)\n mytz = self._tzinfo\n ottz = other._tzinfo\n myoff = otoff = None\n\n if mytz is ottz:\n base_compare = True\n else:\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n # Assume that allow_mixed means that we are called from __eq__\n if allow_mixed:\n if myoff != self.replace(fold=not self.fold).utcoffset():\n return 2\n if otoff != other.replace(fold=not other.fold).utcoffset():\n return 2\n base_compare = myoff == otoff\n\n if base_compare:\n return _cmp((self._year, self._month, self._day,\n self._hour, self._minute, self._second,\n self._microsecond),\n (other._year, other._month, other._day,\n other._hour, other._minute, other._second,\n other._microsecond))\n if myoff is None or otoff is None:\n if allow_mixed:\n return 2 # arbitrary non-zero value\n else:\n raise TypeError(\"cannot compare naive and aware datetimes\")\n # XXX What follows could be done more efficiently...\n diff = self - other # this will take offsets into account\n if diff.days < 0:\n return -1\n return diff and 1 or 0" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n \"Add a datetime and a timedelta.\"\n if not isinstance(other, timedelta):\n return NotImplemented\n delta = timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)\n delta += other\n hour, rem = divmod(delta.seconds, 3600)\n minute, second = divmod(rem, 60)\n if 0 < delta.days <= _MAXORDINAL:\n return type(self).combine(date.fromordinal(delta.days),\n time(hour, minute, second,\n delta.microseconds,\n tzinfo=self._tzinfo))\n raise OverflowError(\"result out of range\")" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__radd__" + ], + [ + "STORE_NAME", + " def __sub__(self, other):\n \"Subtract two datetimes, or a datetime and a timedelta.\"\n if not isinstance(other, datetime):\n if isinstance(other, timedelta):\n return self + -other\n return NotImplemented\n\n days1 = self.toordinal()\n days2 = other.toordinal()\n secs1 = self._second + self._minute * 60 + self._hour * 3600\n secs2 = other._second + other._minute * 60 + other._hour * 3600\n base = timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)\n if self._tzinfo is other._tzinfo:\n return base\n myoff = self.utcoffset()\n otoff = other.utcoffset()\n if myoff == otoff:\n return base\n if myoff is None or otoff is None:\n raise TypeError(\"cannot mix naive and timezone-aware time\")\n return base + otoff - myoff" + ], + [ + "STORE_NAME", + " def __hash__(self):\n if self._hashcode == -1:\n if self.fold:\n t = self.replace(fold=0)\n else:\n t = self\n tzoff = t.utcoffset()\n if tzoff is None:\n self._hashcode = hash(t._getstate()[0])\n else:\n days = _ymd2ord(self.year, self.month, self.day)\n seconds = self.hour * 3600 + self.minute * 60 + self.second\n self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n return self._hashcode" + ], + [ + "STORE_NAME", + " def _getstate(self, protocol=3):\n yhi, ylo = divmod(self._year, 256)\n us2, us3 = divmod(self._microsecond, 256)\n us1, us2 = divmod(us2, 256)\n m = self._month\n if self._fold and protocol > 3:\n m += 128\n basestate = bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])\n if self._tzinfo is None:\n return (basestate,)\n else:\n return (basestate, self._tzinfo)" + ], + [ + "STORE_NAME", + " def __setstate(self, string, tzinfo):\n if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):\n raise TypeError(\"bad tzinfo state arg\")\n (yhi, ylo, m, self._day, self._hour,\n self._minute, self._second, us1, us2, us3) = string\n if m > 127:\n self._fold = 1\n self._month = m - 128\n else:\n self._fold = 0\n self._month = m\n self._year = yhi * 256 + ylo\n self._microsecond = (((us1 << 8) | us2) << 8) | us3\n self._tzinfo = tzinfo" + ], + [ + "STORE_NAME", + " def __reduce_ex__(self, protocol):\n return (self.__class__, self._getstate(protocol))" + ], + [ + "STORE_NAME", + " def __reduce__(self):\n return self.__reduce_ex__(2)" + ], [ "LOAD_GLOBAL", "isinstance" @@ -10887,6 +13467,10 @@ "CALL_FUNCTION", "bytes(year, 'latin1')" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_GLOBAL", "UnicodeEncodeError" @@ -10915,6 +13499,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -10967,6 +13555,18 @@ "CALL_FUNCTION", "_check_date_fields(year, month, day)" ], + [ + "STORE_FAST", + "year" + ], + [ + "STORE_FAST", + "month" + ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_GLOBAL", "_check_time_fields" @@ -10995,6 +13595,26 @@ "CALL_FUNCTION", "_check_time_fields(\n hour, minute, second, microsecond, fold)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], + [ + "STORE_FAST", + "microsecond" + ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "_check_tzinfo_arg" @@ -11023,6 +13643,10 @@ "CALL_METHOD", "object.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "year" @@ -11207,6 +13831,14 @@ "CALL_METHOD", "_math.modf(t)" ], + [ + "STORE_FAST", + "frac" + ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_GLOBAL", "round" @@ -11223,6 +13855,10 @@ "CALL_FUNCTION", "round(frac * 1e6)" ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "us" @@ -11231,6 +13867,22 @@ "COMPARE_OP", "us >= 1000000" ], + [ + "LOAD_FAST", + "t" + ], + [ + "STORE_FAST", + "t" + ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "us" @@ -11239,6 +13891,22 @@ "COMPARE_OP", "us < 0" ], + [ + "LOAD_FAST", + "t" + ], + [ + "STORE_FAST", + "t" + ], + [ + "LOAD_FAST", + "us" + ], + [ + "STORE_FAST", + "us" + ], [ "LOAD_FAST", "utc" @@ -11259,6 +13927,10 @@ "LOAD_ATTR", "_time.localtime" ], + [ + "STORE_FAST", + "converter" + ], [ "LOAD_FAST", "converter" @@ -11271,6 +13943,42 @@ "CALL_FUNCTION", "converter(t)" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], + [ + "STORE_FAST", + "weekday" + ], + [ + "STORE_FAST", + "jday" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "min" @@ -11283,6 +13991,10 @@ "CALL_FUNCTION", "min(ss, 59)" ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11323,6 +14035,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "tz" @@ -11335,6 +14051,10 @@ "LOAD_FAST", "utc" ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "t" @@ -11388,8 +14108,32 @@ "converter(t - max_fold_seconds)" ], [ - "BINARY_SUBSCR", - "converter(t - max_fold_seconds)[:6]" + "BINARY_SUBSCR", + "converter(t - max_fold_seconds)[:6]" + ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" ], [ "LOAD_FAST", @@ -11431,6 +14175,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe1" + ], [ "LOAD_FAST", "result" @@ -11459,6 +14207,10 @@ "BINARY_SUBTRACT", "result - probe1 - timedelta(0, max_fold_seconds)" ], + [ + "STORE_FAST", + "trans" + ], [ "LOAD_FAST", "trans" @@ -11507,6 +14259,30 @@ "BINARY_SUBSCR", "converter(t + trans // timedelta(0, 1))[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_FAST", "cls" @@ -11547,6 +14323,10 @@ "CALL_FUNCTION", "cls(y, m, d, hh, mm, ss, us, tz)" ], + [ + "STORE_FAST", + "probe2" + ], [ "LOAD_FAST", "probe2" @@ -11591,6 +14371,10 @@ "CALL_METHOD", "tz.fromutc(result)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -11663,6 +14447,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11695,6 +14483,10 @@ "CALL_METHOD", "_time.time()" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "cls" @@ -11775,6 +14567,10 @@ "LOAD_ATTR", "time.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "cls" @@ -11899,6 +14695,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(f'Invalid isoformat string: {date_string!r}')" @@ -11915,6 +14715,10 @@ "CALL_FUNCTION", "_find_isoformat_datetime_separator(date_string)" ], + [ + "STORE_FAST", + "separator_location" + ], [ "LOAD_FAST", "date_string" @@ -11927,6 +14731,10 @@ "BINARY_SUBSCR", "date_string[0:separator_location]" ], + [ + "STORE_FAST", + "dstr" + ], [ "LOAD_FAST", "date_string" @@ -11943,6 +14751,10 @@ "BINARY_SUBSCR", "date_string[(separator_location+1):]" ], + [ + "STORE_FAST", + "tstr" + ], [ "LOAD_GLOBAL", "_parse_isoformat_date" @@ -11955,6 +14767,10 @@ "CALL_FUNCTION", "_parse_isoformat_date(dstr)" ], + [ + "STORE_FAST", + "date_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -11967,6 +14783,10 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" @@ -11987,6 +14807,10 @@ "CALL_FUNCTION", "_parse_isoformat_time(tstr)" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_GLOBAL", "ValueError" @@ -11999,10 +14823,18 @@ "LOAD_FAST", "date_string" ], + [ + "BUILD_STRING", + "f'Invalid isoformat string: {date_string!r}'" + ], [ "CALL_FUNCTION", "ValueError(\n f'Invalid isoformat string: {date_string!r}')" ], + [ + "STORE_FAST", + "time_components" + ], [ "LOAD_FAST", "cls" @@ -12035,6 +14867,10 @@ "CALL_METHOD", "self.dst()" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" @@ -12043,10 +14879,22 @@ "IS_OP", "dst is None" ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_FAST", "dst" ], + [ + "STORE_FAST", + "dst" + ], + [ + "STORE_FAST", + "dst" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12115,6 +14963,14 @@ "CALL_FUNCTION", "datetime(1970, 1, 1)" ], + [ + "STORE_DEREF", + "epoch" + ], + [ + "STORE_FAST", + "max_fold_seconds" + ], [ "LOAD_FAST", "self" @@ -12139,6 +14995,14 @@ "BINARY_FLOOR_DIVIDE", "(self - epoch) // timedelta(0, 1)" ], + [ + "STORE_FAST", + "t" + ], + [ + "STORE_FAST", + " def local(u):\n y, m, d, hh, mm, ss = _time.localtime(u)[:6]\n return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)" + ], [ "LOAD_FAST", "local" @@ -12159,6 +15023,10 @@ "BINARY_SUBTRACT", "local(t) - t" ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_FAST", "t" @@ -12171,6 +15039,10 @@ "BINARY_SUBTRACT", "t - a" ], + [ + "STORE_FAST", + "u1" + ], [ "LOAD_FAST", "local" @@ -12183,6 +15055,10 @@ "CALL_FUNCTION", "local(u1)" ], + [ + "STORE_FAST", + "t1" + ], [ "LOAD_FAST", "t1" @@ -12227,6 +15103,10 @@ "BINARY_ADD", "u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12247,6 +15127,10 @@ "BINARY_SUBTRACT", "local(u2) - u2" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12275,6 +15159,10 @@ "BINARY_SUBTRACT", "t1 - u1" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_FAST", "a" @@ -12299,6 +15187,10 @@ "BINARY_SUBTRACT", "t - b" ], + [ + "STORE_FAST", + "u2" + ], [ "LOAD_FAST", "local" @@ -12311,6 +15203,10 @@ "CALL_FUNCTION", "local(u2)" ], + [ + "STORE_FAST", + "t2" + ], [ "LOAD_FAST", "t2" @@ -12395,6 +15291,30 @@ "BINARY_SUBSCR", "_time.localtime(u)[:6]" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "datetime" @@ -12471,6 +15391,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -12523,14 +15447,26 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "offset" ], + [ + "LOAD_FAST", + "self" + ], [ "LOAD_FAST", "offset" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "self" @@ -12555,6 +15491,18 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "y" + ], + [ + "STORE_FAST", + "m" + ], + [ + "STORE_FAST", + "d" + ], [ "LOAD_FAST", "self" @@ -12579,6 +15527,18 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "hh" + ], + [ + "STORE_FAST", + "mm" + ], + [ + "STORE_FAST", + "ss" + ], [ "LOAD_GLOBAL", "_build_struct_time" @@ -12763,6 +15723,10 @@ "LOAD_ATTR", "self.year" ], + [ + "STORE_FAST", + "year" + ], [ "LOAD_FAST", "month" @@ -12779,6 +15743,10 @@ "LOAD_ATTR", "self.month" ], + [ + "STORE_FAST", + "month" + ], [ "LOAD_FAST", "day" @@ -12795,6 +15763,10 @@ "LOAD_ATTR", "self.day" ], + [ + "STORE_FAST", + "day" + ], [ "LOAD_FAST", "hour" @@ -12811,6 +15783,10 @@ "LOAD_ATTR", "self.hour" ], + [ + "STORE_FAST", + "hour" + ], [ "LOAD_FAST", "minute" @@ -12827,6 +15803,10 @@ "LOAD_ATTR", "self.minute" ], + [ + "STORE_FAST", + "minute" + ], [ "LOAD_FAST", "second" @@ -12843,6 +15823,10 @@ "LOAD_ATTR", "self.second" ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "microsecond" @@ -12859,6 +15843,10 @@ "LOAD_ATTR", "self.microsecond" ], + [ + "STORE_FAST", + "microsecond" + ], [ "LOAD_FAST", "tzinfo" @@ -12875,6 +15863,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "tzinfo" + ], [ "LOAD_FAST", "fold" @@ -12891,6 +15883,10 @@ "LOAD_ATTR", "self.fold" ], + [ + "STORE_FAST", + "fold" + ], [ "LOAD_GLOBAL", "type" @@ -12967,6 +15963,10 @@ "CALL_METHOD", "self._mktime()" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_FAST", "self" @@ -12991,6 +15991,10 @@ "BINARY_FLOOR_DIVIDE", "(self - _EPOCH) // timedelta(seconds=1)" ], + [ + "STORE_FAST", + "ts" + ], [ "LOAD_GLOBAL", "_time" @@ -13007,6 +16011,10 @@ "CALL_METHOD", "_time.localtime(ts)" ], + [ + "STORE_FAST", + "localtm" + ], [ "LOAD_GLOBAL", "datetime" @@ -13023,6 +16031,10 @@ "CALL_FUNCTION_EX", "datetime(*localtm[:6])" ], + [ + "STORE_FAST", + "local" + ], [ "LOAD_FAST", "localtm" @@ -13031,6 +16043,10 @@ "LOAD_ATTR", "localtm.tm_gmtoff" ], + [ + "STORE_FAST", + "gmtoff" + ], [ "LOAD_FAST", "localtm" @@ -13039,6 +16055,10 @@ "LOAD_ATTR", "localtm.tm_zone" ], + [ + "STORE_FAST", + "zone" + ], [ "LOAD_GLOBAL", "timezone" @@ -13083,6 +16103,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_GLOBAL", "isinstance" @@ -13115,6 +16139,10 @@ "LOAD_ATTR", "self.tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13135,6 +16163,10 @@ "CALL_METHOD", "self._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13151,6 +16183,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "mytz" @@ -13167,6 +16203,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "myoffset" @@ -13195,6 +16235,10 @@ "CALL_METHOD", "self.replace(tzinfo=None)._local_timezone()" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "mytz" @@ -13211,6 +16255,10 @@ "CALL_METHOD", "mytz.utcoffset(self)" ], + [ + "STORE_FAST", + "myoffset" + ], [ "LOAD_FAST", "tz" @@ -13251,6 +16299,10 @@ "CALL_FUNCTION_KW", "(self - myoffset).replace(tzinfo=tz)" ], + [ + "STORE_FAST", + "utc" + ], [ "LOAD_FAST", "tz" @@ -13283,6 +16335,10 @@ "BINARY_MODULO", "self.toordinal() % 7" ], + [ + "STORE_FAST", + "weekday" + ], [ "LOAD_GLOBAL", "_DAYNAMES" @@ -13435,6 +16491,10 @@ "BINARY_ADD", "\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n _format_time(self._hour, self._minute, self._second,\n self._microsecond, timespec)" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13447,6 +16507,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "off" + ], [ "LOAD_GLOBAL", "_format_offset" @@ -13459,14 +16523,26 @@ "CALL_FUNCTION", "_format_offset(off)" ], + [ + "STORE_FAST", + "tz" + ], [ "LOAD_FAST", "tz" ], + [ + "LOAD_FAST", + "s" + ], [ "LOAD_FAST", "tz" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -13527,6 +16603,10 @@ "LOAD_ATTR", "self._microsecond" ], + [ + "STORE_FAST", + "L" + ], [ "LOAD_FAST", "L" @@ -13543,6 +16623,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "L" @@ -13559,6 +16643,10 @@ "LOAD_FAST", "L" ], + [ + "DELETE_SUBSCR", + "L[-1]" + ], [ "LOAD_FAST", "self" @@ -13611,6 +16699,10 @@ "BINARY_MODULO", "\"%s.%s(%s)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n \", \".join(map(str, L)))" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13663,6 +16755,10 @@ "BINARY_ADD", "s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "self" @@ -13695,6 +16791,10 @@ "BINARY_ADD", "s[:-1] + \", fold=1)\"" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_FAST", "s" @@ -13711,6 +16811,10 @@ "CALL_FUNCTION_KW", "self.isoformat(sep=' ')" ], + [ + "STORE_FAST", + "import _strptime" + ], [ "LOAD_FAST", "_strptime" @@ -13767,6 +16871,10 @@ "CALL_METHOD", "self._tzinfo.utcoffset(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -13815,6 +16923,10 @@ "CALL_METHOD", "self._tzinfo.tzname(self)" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "_check_tzname" @@ -13863,6 +16975,10 @@ "CALL_METHOD", "self._tzinfo.dst(self)" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_GLOBAL", "_check_utc_offset" @@ -14247,6 +17363,10 @@ "LOAD_ATTR", "self._tzinfo" ], + [ + "STORE_FAST", + "mytz" + ], [ "LOAD_FAST", "other" @@ -14255,6 +17375,18 @@ "LOAD_ATTR", "other._tzinfo" ], + [ + "STORE_FAST", + "ottz" + ], + [ + "STORE_FAST", + "myoff" + ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "mytz" @@ -14267,6 +17399,10 @@ "IS_OP", "mytz is ottz" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "self" @@ -14279,6 +17415,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -14291,6 +17431,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "allow_mixed" @@ -14387,6 +17531,10 @@ "COMPARE_OP", "myoff == otoff" ], + [ + "STORE_FAST", + "base_compare" + ], [ "LOAD_FAST", "base_compare" @@ -14551,6 +17699,10 @@ "BINARY_SUBTRACT", "self - other" ], + [ + "STORE_FAST", + "diff" + ], [ "LOAD_FAST", "diff" @@ -14639,10 +17791,22 @@ "CALL_FUNCTION_KW", "timedelta(self.toordinal(),\n hours=self._hour,\n minutes=self._minute,\n seconds=self._second,\n microseconds=self._microsecond)" ], + [ + "STORE_FAST", + "delta" + ], + [ + "LOAD_FAST", + "delta" + ], [ "LOAD_FAST", "other" ], + [ + "STORE_FAST", + "delta" + ], [ "LOAD_GLOBAL", "divmod" @@ -14659,6 +17823,14 @@ "CALL_FUNCTION", "divmod(delta.seconds, 3600)" ], + [ + "STORE_FAST", + "hour" + ], + [ + "STORE_FAST", + "rem" + ], [ "LOAD_GLOBAL", "divmod" @@ -14671,6 +17843,14 @@ "CALL_FUNCTION", "divmod(rem, 60)" ], + [ + "STORE_FAST", + "minute" + ], + [ + "STORE_FAST", + "second" + ], [ "LOAD_FAST", "delta" @@ -14831,6 +18011,10 @@ "CALL_METHOD", "self.toordinal()" ], + [ + "STORE_FAST", + "days1" + ], [ "LOAD_FAST", "other" @@ -14843,6 +18027,10 @@ "CALL_METHOD", "other.toordinal()" ], + [ + "STORE_FAST", + "days2" + ], [ "LOAD_FAST", "self" @@ -14883,6 +18071,10 @@ "BINARY_ADD", "self._second + self._minute * 60 + self._hour * 3600" ], + [ + "STORE_FAST", + "secs1" + ], [ "LOAD_FAST", "other" @@ -14923,6 +18115,10 @@ "BINARY_ADD", "other._second + other._minute * 60 + other._hour * 3600" ], + [ + "STORE_FAST", + "secs2" + ], [ "LOAD_GLOBAL", "timedelta" @@ -14975,6 +18171,10 @@ "CALL_FUNCTION", "timedelta(days1 - days2,\n secs1 - secs2,\n self._microsecond - other._microsecond)" ], + [ + "STORE_FAST", + "base" + ], [ "LOAD_FAST", "self" @@ -15011,6 +18211,10 @@ "CALL_METHOD", "self.utcoffset()" ], + [ + "STORE_FAST", + "myoff" + ], [ "LOAD_FAST", "other" @@ -15023,6 +18227,10 @@ "CALL_METHOD", "other.utcoffset()" ], + [ + "STORE_FAST", + "otoff" + ], [ "LOAD_FAST", "myoff" @@ -15115,10 +18323,18 @@ "CALL_FUNCTION_KW", "self.replace(fold=0)" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_FAST", + "t" + ], [ "LOAD_FAST", "t" @@ -15131,6 +18347,10 @@ "CALL_METHOD", "t.utcoffset()" ], + [ + "STORE_FAST", + "tzoff" + ], [ "LOAD_FAST", "tzoff" @@ -15203,6 +18423,10 @@ "CALL_FUNCTION", "_ymd2ord(self.year, self.month, self.day)" ], + [ + "STORE_FAST", + "days" + ], [ "LOAD_FAST", "self" @@ -15243,6 +18467,10 @@ "BINARY_ADD", "self.hour * 3600 + self.minute * 60 + self.second" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_GLOBAL", "hash" @@ -15315,6 +18543,14 @@ "CALL_FUNCTION", "divmod(self._year, 256)" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], [ "LOAD_GLOBAL", "divmod" @@ -15331,6 +18567,14 @@ "CALL_FUNCTION", "divmod(self._microsecond, 256)" ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_GLOBAL", "divmod" @@ -15343,6 +18587,14 @@ "CALL_FUNCTION", "divmod(us2, 256)" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], [ "LOAD_FAST", "self" @@ -15351,6 +18603,10 @@ "LOAD_ATTR", "self._month" ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15367,6 +18623,14 @@ "COMPARE_OP", "protocol > 3" ], + [ + "LOAD_FAST", + "m" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_GLOBAL", "bytes" @@ -15431,6 +18695,10 @@ "CALL_FUNCTION", "bytes([yhi, ylo, m, self._day,\n self._hour, self._minute, self._second,\n us1, us2, us3])" ], + [ + "STORE_FAST", + "basestate" + ], [ "LOAD_FAST", "self" @@ -15495,6 +18763,18 @@ "LOAD_FAST", "string" ], + [ + "STORE_FAST", + "yhi" + ], + [ + "STORE_FAST", + "ylo" + ], + [ + "STORE_FAST", + "m" + ], [ "LOAD_FAST", "self" @@ -15527,6 +18807,18 @@ "STORE_ATTR", "self._second" ], + [ + "STORE_FAST", + "us1" + ], + [ + "STORE_FAST", + "us2" + ], + [ + "STORE_FAST", + "us3" + ], [ "LOAD_FAST", "m" @@ -15687,6 +18979,10 @@ "CALL_METHOD", "self.__reduce_ex__(2)" ], + [ + "STORE_FAST", + "THURSDAY" + ], [ "LOAD_GLOBAL", "_ymd2ord" @@ -15699,6 +18995,10 @@ "CALL_FUNCTION", "_ymd2ord(year, 1, 1)" ], + [ + "STORE_FAST", + "firstday" + ], [ "LOAD_FAST", "firstday" @@ -15711,6 +19011,10 @@ "BINARY_MODULO", "(firstday + 6) % 7" ], + [ + "STORE_FAST", + "firstweekday" + ], [ "LOAD_FAST", "firstday" @@ -15723,6 +19027,10 @@ "BINARY_SUBTRACT", "firstday - firstweekday" ], + [ + "STORE_FAST", + "week1monday" + ], [ "LOAD_FAST", "firstweekday" @@ -15739,6 +19047,30 @@ "LOAD_FAST", "week1monday" ], + [ + "STORE_FAST", + "week1monday" + ], + [ + "LOAD_FAST", + "week1monday" + ], + [ + "LOAD_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "class timezone(tzinfo):\n __slots__ = '_offset', '_name'\n\n # Sentinel value to disallow None\n _Omitted = object()\n def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)\n\n @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self\n\n def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)\n\n def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented\n\n def __hash__(self):\n return hash(self._offset)\n\n def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)\n\n def __str__(self):\n return self.tzname(None)\n\n def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")\n\n def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")\n\n def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")\n\n def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")\n\n _maxoffset = timedelta(hours=24, microseconds=-1)\n _minoffset = -_maxoffset\n\n @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], + [ + "STORE_NAME", + "__slots__" + ], [ "LOAD_NAME", "object" @@ -15747,10 +19079,18 @@ "CALL_FUNCTION", "object()" ], + [ + "STORE_NAME", + "_Omitted" + ], [ "LOAD_NAME", "_Omitted" ], + [ + "STORE_NAME", + " def __new__(cls, offset, name=_Omitted):\n if not isinstance(offset, timedelta):\n raise TypeError(\"offset must be a timedelta\")\n if name is cls._Omitted:\n if not offset:\n return cls.utc\n name = None\n elif not isinstance(name, str):\n raise TypeError(\"name must be a string\")\n if not cls._minoffset <= offset <= cls._maxoffset:\n raise ValueError(\"offset must be a timedelta \"\n \"strictly between -timedelta(hours=24) and \"\n \"timedelta(hours=24).\")\n return cls._create(offset, name)" + ], [ "LOAD_NAME", "classmethod" @@ -15759,6 +19099,46 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _create(cls, offset, name=None):\n self = tzinfo.__new__(cls)\n self._offset = offset\n self._name = name\n return self" + ], + [ + "STORE_NAME", + " def __getinitargs__(self):\n \"\"\"pickle support\"\"\"\n if self._name is None:\n return (self._offset,)\n return (self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __eq__(self, other):\n if isinstance(other, timezone):\n return self._offset == other._offset\n return NotImplemented" + ], + [ + "STORE_NAME", + " def __hash__(self):\n return hash(self._offset)" + ], + [ + "STORE_NAME", + " def __repr__(self):\n \"\"\"Convert to formal string, for repr().\n\n >>> tz = timezone.utc\n >>> repr(tz)\n 'datetime.timezone.utc'\n >>> tz = timezone(timedelta(hours=-5), 'EST')\n >>> repr(tz)\n \"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')\"\n \"\"\"\n if self is self.utc:\n return 'datetime.timezone.utc'\n if self._name is None:\n return \"%s.%s(%r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset)\n return \"%s.%s(%r, %r)\" % (self.__class__.__module__,\n self.__class__.__qualname__,\n self._offset, self._name)" + ], + [ + "STORE_NAME", + " def __str__(self):\n return self.tzname(None)" + ], + [ + "STORE_NAME", + " def utcoffset(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return self._offset\n raise TypeError(\"utcoffset() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def tzname(self, dt):\n if isinstance(dt, datetime) or dt is None:\n if self._name is None:\n return self._name_from_offset(self._offset)\n return self._name\n raise TypeError(\"tzname() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def dst(self, dt):\n if isinstance(dt, datetime) or dt is None:\n return None\n raise TypeError(\"dst() argument must be a datetime instance\"\n \" or None\")" + ], + [ + "STORE_NAME", + " def fromutc(self, dt):\n if isinstance(dt, datetime):\n if dt.tzinfo is not self:\n raise ValueError(\"fromutc: dt.tzinfo \"\n \"is not self\")\n return dt + self._offset\n raise TypeError(\"fromutc() argument must be a datetime instance\"\n \" or None\")" + ], [ "LOAD_NAME", "timedelta" @@ -15767,6 +19147,10 @@ "CALL_FUNCTION_KW", "timedelta(hours=24, microseconds=-1)" ], + [ + "STORE_NAME", + "_maxoffset" + ], [ "LOAD_NAME", "_maxoffset" @@ -15775,6 +19159,10 @@ "UNARY_NEGATIVE", "-_maxoffset" ], + [ + "STORE_NAME", + "_minoffset" + ], [ "LOAD_NAME", "staticmethod" @@ -15783,6 +19171,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _name_from_offset(delta):\n if not delta:\n return 'UTC'\n if delta < timedelta(0):\n sign = '-'\n delta = -delta\n else:\n sign = '+'\n hours, rest = divmod(delta, timedelta(hours=1))\n minutes, rest = divmod(rest, timedelta(minutes=1))\n seconds = rest.seconds\n microseconds = rest.microseconds\n if microseconds:\n return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}')\n if seconds:\n return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n return f'UTC{sign}{hours:02d}:{minutes:02d}'" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15835,6 +19227,10 @@ "LOAD_ATTR", "cls.utc" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_GLOBAL", "isinstance" @@ -15923,6 +19319,10 @@ "CALL_METHOD", "tzinfo.__new__(cls)" ], + [ + "STORE_FAST", + "self" + ], [ "LOAD_FAST", "offset" @@ -16391,6 +19791,10 @@ "COMPARE_OP", "delta < timedelta(0)" ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_FAST", "delta" @@ -16399,6 +19803,14 @@ "UNARY_NEGATIVE", "-delta" ], + [ + "STORE_FAST", + "delta" + ], + [ + "STORE_FAST", + "sign" + ], [ "LOAD_GLOBAL", "divmod" @@ -16419,6 +19831,14 @@ "CALL_FUNCTION", "divmod(delta, timedelta(hours=1))" ], + [ + "STORE_FAST", + "hours" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_GLOBAL", "divmod" @@ -16439,6 +19859,14 @@ "CALL_FUNCTION", "divmod(rest, timedelta(minutes=1))" ], + [ + "STORE_FAST", + "minutes" + ], + [ + "STORE_FAST", + "rest" + ], [ "LOAD_FAST", "rest" @@ -16447,6 +19875,10 @@ "LOAD_ATTR", "rest.seconds" ], + [ + "STORE_FAST", + "seconds" + ], [ "LOAD_FAST", "rest" @@ -16455,6 +19887,10 @@ "LOAD_ATTR", "rest.microseconds" ], + [ + "STORE_FAST", + "microseconds" + ], [ "LOAD_FAST", "microseconds" @@ -16479,6 +19915,10 @@ "LOAD_FAST", "microseconds" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'\n f'.{microseconds:06d}'" + ], [ "LOAD_FAST", "seconds" @@ -16499,6 +19939,10 @@ "LOAD_FAST", "seconds" ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}'" + ], [ "LOAD_FAST", "sign" @@ -16510,5 +19954,9 @@ [ "LOAD_FAST", "minutes" + ], + [ + "BUILD_STRING", + "f'UTC{sign}{hours:02d}:{minutes:02d}'" ] ] \ No newline at end of file diff --git a/tests/sample_results/db-py-3.10.json b/tests/sample_results/db-py-3.10.json index fd27b11..9dbcbff 100644 --- a/tests/sample_results/db-py-3.10.json +++ b/tests/sample_results/db-py-3.10.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,114 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "from typing import List" + ], + [ + "STORE_NAME", + "from contextlib import contextmanager" + ], + [ + "STORE_NAME", + "from humanize import naturaltime" + ], + [ + "STORE_NAME", + "from markupsafe import Markup" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql import LONGTEXT" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql.base import RESERVED_WORDS" + ], [ "LOAD_NAME", "RESERVED_WORDS" @@ -23,10 +175,22 @@ "CALL_METHOD", "RESERVED_WORDS.add('function')" ], + [ + "STORE_NAME", + "DB_VERSION" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_NAME", "retry" @@ -51,6 +215,38 @@ "CALL_FUNCTION", "retry(3, (InterfaceError, OperationalError, InternalError, ProgrammingError))" ], + [ + "STORE_NAME", + "retry_db" + ], + [ + "LOAD_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" + ], + [ + "STORE_NAME", + " def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)" + ], + [ + "STORE_NAME", + " def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths" + ], + [ + "STORE_NAME", + " def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)" + ], [ "LOAD_NAME", "contextmanager" @@ -59,6 +255,14 @@ "CALL_FUNCTION", "contextmanager" ], + [ + "STORE_NAME", + " @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()" + ], + [ + "STORE_NAME", + " def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_FAST", "db_uri" @@ -119,6 +323,10 @@ "STORE_ATTR", "self.db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "dict" @@ -127,6 +335,10 @@ "CALL_FUNCTION_KW", "dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )" ], + [ + "STORE_FAST", + "kwargs" + ], [ "LOAD_GLOBAL", "create_engine" @@ -143,6 +355,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_GLOBAL", "ArgumentError" @@ -155,6 +371,10 @@ "BINARY_ADD", "'sqlite:///' + db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "create_engine" @@ -171,6 +391,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_FAST", "engine" @@ -207,6 +431,14 @@ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_FAST", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_GLOBAL", "declarative_base" @@ -219,18 +451,42 @@ "CALL_FUNCTION_KW", "declarative_base(cls=Base)" ], + [ + "STORE_FAST", + "Base" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_DEREF", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_DEREF", + "db_self" + ], [ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_FAST", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], [ "LOAD_FAST", "engine" @@ -251,14 +507,34 @@ "LOAD_GLOBAL", "Text" ], + [ + "STORE_DEREF", + "LongText" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_FAST", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_FAST", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_FAST", "Call" @@ -311,6 +587,10 @@ "STORE_ATTR", "self.key_value_store" ], + [ + "STORE_FAST", + "kv" + ], [ "LOAD_FAST", "_skip_version_check" @@ -415,6 +695,18 @@ "CALL_METHOD", "sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" ], + [ + "LOAD_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_NAME", "declared_attr" @@ -423,6 +715,10 @@ "CALL_FUNCTION", "declared_attr" ], + [ + "STORE_NAME", + " @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_FAST", "cls" @@ -439,6 +735,18 @@ "CALL_METHOD", "cls.__name__.lower()" ], + [ + "LOAD_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_NAME", "Column" @@ -455,6 +763,10 @@ "CALL_FUNCTION_KW", "Column(String(50), primary_key=True)" ], + [ + "STORE_NAME", + "key" + ], [ "LOAD_NAME", "Column" @@ -467,14 +779,46 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "value" + ], + [ + "LOAD_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())" + ], + [ + "STORE_NAME", + " def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))" + ], [ "LOAD_NAME", "__getitem__" ], + [ + "STORE_NAME", + "__getattr__" + ], [ "LOAD_NAME", "__setitem__" ], + [ + "STORE_NAME", + "__setattr__" + ], [ "LOAD_DEREF", "db_self" @@ -487,6 +831,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -527,6 +875,10 @@ "CALL_METHOD", "session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar()" ], + [ + "CALL_FUNCTION", + " with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())" + ], [ "LOAD_DEREF", "db_self" @@ -539,6 +891,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -611,6 +967,22 @@ "CALL_METHOD", "session.add(KeyValue(key=key, value=str(value)))" ], + [ + "CALL_FUNCTION", + " with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))" + ], + [ + "LOAD_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_NAME", "Column" @@ -627,6 +999,10 @@ "CALL_FUNCTION_KW", "Column(String(length=32), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -647,6 +1023,10 @@ "CALL_FUNCTION_KW", "Column(Integer, ForeignKey('function.id'), index=True)" ], + [ + "STORE_NAME", + "function_id" + ], [ "LOAD_NAME", "relationship" @@ -663,6 +1043,10 @@ "CALL_FUNCTION_KW", "relationship('Function', backref=backref('calls', lazy='dynamic'))" ], + [ + "STORE_NAME", + "function" + ], [ "LOAD_NAME", "Column" @@ -675,6 +1059,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "arguments" + ], [ "LOAD_NAME", "Column" @@ -687,6 +1075,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "return_value" + ], [ "LOAD_NAME", "Column" @@ -699,6 +1091,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "exception" + ], [ "LOAD_NAME", "Column" @@ -711,6 +1107,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "traceback" + ], [ "LOAD_NAME", "Column" @@ -723,6 +1123,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -735,6 +1139,10 @@ "CALL_FUNCTION_KW", "Column(DateTime, index=True)" ], + [ + "STORE_NAME", + "start_time" + ], [ "LOAD_NAME", "property" @@ -743,6 +1151,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)" + ], [ "LOAD_NAME", "staticmethod" @@ -751,6 +1163,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))" + ], [ "LOAD_NAME", "property" @@ -759,6 +1175,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))" + ], [ "LOAD_NAME", "property" @@ -767,6 +1187,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True" + ], [ "LOAD_NAME", "property" @@ -775,6 +1199,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)" + ], [ "LOAD_NAME", "property" @@ -783,6 +1211,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def arguments_list(self):\n return json.loads(self.arguments)" + ], [ "LOAD_NAME", "property" @@ -791,6 +1223,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -799,6 +1235,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" + ], [ "LOAD_NAME", "id" @@ -827,6 +1267,10 @@ "LOAD_NAME", "arguments" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_FAST", "self" @@ -1059,6 +1503,18 @@ "CALL_FUNCTION_EX", "dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" ], + [ + "LOAD_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_NAME", "Column" @@ -1079,6 +1535,10 @@ "CALL_FUNCTION_KW", "Column(Integer, Sequence('function_id_seq'), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -1091,6 +1551,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "file" + ], [ "LOAD_NAME", "Column" @@ -1103,6 +1567,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "name" + ], [ "LOAD_NAME", "Column" @@ -1115,6 +1583,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "type" + ], [ "LOAD_NAME", "Column" @@ -1127,6 +1599,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "html_body" + ], [ "LOAD_NAME", "Column" @@ -1139,6 +1615,10 @@ "CALL_FUNCTION", "Column(Integer)" ], + [ + "STORE_NAME", + "lineno" + ], [ "LOAD_NAME", "Column" @@ -1151,6 +1631,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -1167,6 +1651,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "hash" + ], [ "LOAD_NAME", "Column" @@ -1183,6 +1671,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "body_hash" + ], [ "LOAD_NAME", "UniqueConstraint" @@ -1207,6 +1699,10 @@ "CALL_FUNCTION_KW", "Index('idx_name', 'name', mysql_length=32)" ], + [ + "STORE_NAME", + "__table_args__" + ], [ "LOAD_NAME", "property" @@ -1215,6 +1711,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -1223,6 +1723,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')" + ], [ "LOAD_NAME", "file" @@ -1247,6 +1751,10 @@ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_GLOBAL", "json" @@ -1327,6 +1835,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1359,6 +1871,18 @@ "CALL_METHOD", "session.query(self.Function.file).distinct()" ], + [ + "CALL_FUNCTION", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "paths" + ], + [ + "CALL_FUNCTION", + " with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], [ "LOAD_FAST", "paths" @@ -1419,6 +1943,14 @@ "LOAD_FAST", "paths" ], + [ + "LOAD_FAST", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -1467,6 +1999,10 @@ "LOAD_ATTR", "self._KeyValue" ], + [ + "STORE_FAST", + "model" + ], [ "LOAD_FAST", "self" @@ -1519,6 +2055,10 @@ "CALL_METHOD", "self.Session()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1591,6 +2131,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)" + ], [ "LOAD_GLOBAL", "retry_db" @@ -1615,6 +2159,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_DEREF", "func" @@ -1634,5 +2182,9 @@ [ "CALL_FUNCTION_EX", "func(session, *args, **kwargs)" + ], + [ + "CALL_FUNCTION", + " with self.session_scope() as session:\n return func(session, *args, **kwargs)" ] ] \ No newline at end of file diff --git a/tests/sample_results/db-py-3.8.json b/tests/sample_results/db-py-3.8.json index 2f78336..b47e1af 100644 --- a/tests/sample_results/db-py-3.8.json +++ b/tests/sample_results/db-py-3.8.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,114 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "from typing import List" + ], + [ + "STORE_NAME", + "from contextlib import contextmanager" + ], + [ + "STORE_NAME", + "from humanize import naturaltime" + ], + [ + "STORE_NAME", + "from markupsafe import Markup" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql import LONGTEXT" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql.base import RESERVED_WORDS" + ], [ "LOAD_NAME", "RESERVED_WORDS" @@ -23,10 +175,22 @@ "CALL_METHOD", "RESERVED_WORDS.add('function')" ], + [ + "STORE_NAME", + "DB_VERSION" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_NAME", "retry" @@ -51,6 +215,38 @@ "CALL_FUNCTION", "retry(3, (InterfaceError, OperationalError, InternalError, ProgrammingError))" ], + [ + "STORE_NAME", + "retry_db" + ], + [ + "LOAD_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" + ], + [ + "STORE_NAME", + " def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)" + ], + [ + "STORE_NAME", + " def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths" + ], + [ + "STORE_NAME", + " def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)" + ], [ "LOAD_NAME", "contextmanager" @@ -59,6 +255,14 @@ "CALL_FUNCTION", "contextmanager" ], + [ + "STORE_NAME", + " @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()" + ], + [ + "STORE_NAME", + " def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_FAST", "db_uri" @@ -119,6 +323,10 @@ "STORE_ATTR", "self.db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "dict" @@ -127,6 +335,10 @@ "CALL_FUNCTION_KW", "dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )" ], + [ + "STORE_FAST", + "kwargs" + ], [ "LOAD_GLOBAL", "create_engine" @@ -143,6 +355,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_GLOBAL", "ArgumentError" @@ -155,6 +371,10 @@ "BINARY_ADD", "'sqlite:///' + db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "create_engine" @@ -171,6 +391,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_FAST", "engine" @@ -207,6 +431,14 @@ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_FAST", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_GLOBAL", "declarative_base" @@ -219,18 +451,42 @@ "CALL_FUNCTION_KW", "declarative_base(cls=Base)" ], + [ + "STORE_FAST", + "Base" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_DEREF", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_DEREF", + "db_self" + ], [ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_FAST", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], [ "LOAD_FAST", "engine" @@ -251,14 +507,34 @@ "LOAD_GLOBAL", "Text" ], + [ + "STORE_DEREF", + "LongText" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_FAST", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_FAST", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_FAST", "Call" @@ -311,6 +587,10 @@ "STORE_ATTR", "self.key_value_store" ], + [ + "STORE_FAST", + "kv" + ], [ "LOAD_FAST", "_skip_version_check" @@ -415,6 +695,18 @@ "CALL_METHOD", "sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" ], + [ + "LOAD_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_NAME", "declared_attr" @@ -423,6 +715,10 @@ "CALL_FUNCTION", "declared_attr" ], + [ + "STORE_NAME", + " @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_FAST", "cls" @@ -439,6 +735,18 @@ "CALL_METHOD", "cls.__name__.lower()" ], + [ + "LOAD_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_NAME", "Column" @@ -455,6 +763,10 @@ "CALL_FUNCTION_KW", "Column(String(50), primary_key=True)" ], + [ + "STORE_NAME", + "key" + ], [ "LOAD_NAME", "Column" @@ -467,14 +779,46 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "value" + ], + [ + "LOAD_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())" + ], + [ + "STORE_NAME", + " def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))" + ], [ "LOAD_NAME", "__getitem__" ], + [ + "STORE_NAME", + "__getattr__" + ], [ "LOAD_NAME", "__setitem__" ], + [ + "STORE_NAME", + "__setattr__" + ], [ "LOAD_DEREF", "db_self" @@ -487,6 +831,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -539,6 +887,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -611,6 +963,18 @@ "CALL_METHOD", "session.add(KeyValue(key=key, value=str(value)))" ], + [ + "LOAD_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_NAME", "Column" @@ -627,6 +991,10 @@ "CALL_FUNCTION_KW", "Column(String(length=32), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -647,6 +1015,10 @@ "CALL_FUNCTION_KW", "Column(Integer, ForeignKey('function.id'), index=True)" ], + [ + "STORE_NAME", + "function_id" + ], [ "LOAD_NAME", "relationship" @@ -663,6 +1035,10 @@ "CALL_FUNCTION_KW", "relationship('Function', backref=backref('calls', lazy='dynamic'))" ], + [ + "STORE_NAME", + "function" + ], [ "LOAD_NAME", "Column" @@ -675,6 +1051,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "arguments" + ], [ "LOAD_NAME", "Column" @@ -687,6 +1067,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "return_value" + ], [ "LOAD_NAME", "Column" @@ -699,6 +1083,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "exception" + ], [ "LOAD_NAME", "Column" @@ -711,6 +1099,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "traceback" + ], [ "LOAD_NAME", "Column" @@ -723,6 +1115,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -735,6 +1131,10 @@ "CALL_FUNCTION_KW", "Column(DateTime, index=True)" ], + [ + "STORE_NAME", + "start_time" + ], [ "LOAD_NAME", "property" @@ -743,6 +1143,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)" + ], [ "LOAD_NAME", "staticmethod" @@ -751,6 +1155,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))" + ], [ "LOAD_NAME", "property" @@ -759,6 +1167,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))" + ], [ "LOAD_NAME", "property" @@ -767,6 +1179,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True" + ], [ "LOAD_NAME", "property" @@ -775,6 +1191,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)" + ], [ "LOAD_NAME", "property" @@ -783,6 +1203,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def arguments_list(self):\n return json.loads(self.arguments)" + ], [ "LOAD_NAME", "property" @@ -791,6 +1215,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -799,6 +1227,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" + ], [ "LOAD_NAME", "id" @@ -827,6 +1259,10 @@ "LOAD_NAME", "arguments" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_FAST", "self" @@ -1051,6 +1487,18 @@ "CALL_FUNCTION_EX", "dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" ], + [ + "LOAD_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_NAME", "Column" @@ -1071,6 +1519,10 @@ "CALL_FUNCTION_KW", "Column(Integer, Sequence('function_id_seq'), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -1083,6 +1535,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "file" + ], [ "LOAD_NAME", "Column" @@ -1095,6 +1551,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "name" + ], [ "LOAD_NAME", "Column" @@ -1107,6 +1567,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "type" + ], [ "LOAD_NAME", "Column" @@ -1119,6 +1583,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "html_body" + ], [ "LOAD_NAME", "Column" @@ -1131,6 +1599,10 @@ "CALL_FUNCTION", "Column(Integer)" ], + [ + "STORE_NAME", + "lineno" + ], [ "LOAD_NAME", "Column" @@ -1143,6 +1615,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -1159,6 +1635,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "hash" + ], [ "LOAD_NAME", "Column" @@ -1175,6 +1655,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "body_hash" + ], [ "LOAD_NAME", "UniqueConstraint" @@ -1199,6 +1683,10 @@ "CALL_FUNCTION_KW", "Index('idx_name', 'name', mysql_length=32)" ], + [ + "STORE_NAME", + "__table_args__" + ], [ "LOAD_NAME", "property" @@ -1207,6 +1695,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -1215,6 +1707,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')" + ], [ "LOAD_NAME", "file" @@ -1239,6 +1735,10 @@ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_GLOBAL", "json" @@ -1319,6 +1819,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1351,6 +1855,14 @@ "CALL_METHOD", "session.query(self.Function.file).distinct()" ], + [ + "CALL_FUNCTION", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "paths" + ], [ "LOAD_FAST", "paths" @@ -1411,6 +1923,14 @@ "LOAD_FAST", "paths" ], + [ + "LOAD_FAST", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -1459,6 +1979,10 @@ "LOAD_ATTR", "self._KeyValue" ], + [ + "STORE_FAST", + "model" + ], [ "LOAD_FAST", "self" @@ -1511,6 +2035,10 @@ "CALL_METHOD", "self.Session()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1571,6 +2099,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)" + ], [ "LOAD_GLOBAL", "retry_db" @@ -1595,6 +2127,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_DEREF", "func" diff --git a/tests/sample_results/db-py-3.9.json b/tests/sample_results/db-py-3.9.json index cadef7c..13bdb7f 100644 --- a/tests/sample_results/db-py-3.9.json +++ b/tests/sample_results/db-py-3.9.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], + [ + "STORE_NAME", + "from sqlalchemy.exc import OperationalError, InterfaceError, InternalError, ProgrammingError, ArgumentError" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,114 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "from typing import List" + ], + [ + "STORE_NAME", + "from contextlib import contextmanager" + ], + [ + "STORE_NAME", + "from humanize import naturaltime" + ], + [ + "STORE_NAME", + "from markupsafe import Markup" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy import Sequence, UniqueConstraint, create_engine, Column, Integer, Text, ForeignKey, DateTime, String, \\\n Index" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.ext.declarative import declarative_base, declared_attr" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.orm import backref, relationship, sessionmaker" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql import LONGTEXT" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from littleutils import select_attrs, retry" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import IPYTHON_FILE_PATH, is_ipython_cell" + ], + [ + "STORE_NAME", + "from sqlalchemy.dialects.mysql.base import RESERVED_WORDS" + ], [ "LOAD_NAME", "RESERVED_WORDS" @@ -23,10 +175,22 @@ "CALL_METHOD", "RESERVED_WORDS.add('function')" ], + [ + "STORE_NAME", + "DB_VERSION" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_NAME", "retry" @@ -51,6 +215,38 @@ "CALL_FUNCTION", "retry(3, (InterfaceError, OperationalError, InternalError, ProgrammingError))" ], + [ + "STORE_NAME", + "retry_db" + ], + [ + "LOAD_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + "class Database(object):\n def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')\n\n def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)\n\n def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths\n\n def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)\n\n @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()\n\n def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], + [ + "STORE_NAME", + " def __init__(self, db_uri=None, _skip_version_check=False):\n self.db_uri = db_uri = (\n db_uri\n or os.environ.get('BIRDSEYE_DB')\n or os.path.join(os.path.expanduser('~'),\n '.birdseye.db'))\n\n kwargs = dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )\n\n try:\n engine = create_engine(db_uri, **kwargs)\n except ArgumentError:\n db_uri = 'sqlite:///' + db_uri\n engine = create_engine(db_uri, **kwargs)\n\n self.engine = engine\n\n self.Session = sessionmaker(bind=engine)\n\n class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\n Base = declarative_base(cls=Base) # type: ignore\n\n class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)\n\n db_self = self\n\n class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__\n\n LongText = LONGTEXT if engine.name == 'mysql' else Text\n\n class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)\n\n class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)\n\n self.Call = Call\n self.Function = Function\n self._KeyValue = KeyValue\n\n self.key_value_store = kv = KeyValueStore()\n\n if _skip_version_check:\n return\n\n if not self.table_exists(Function):\n Base.metadata.create_all(engine)\n kv.version = DB_VERSION\n elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION:\n sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" + ], + [ + "STORE_NAME", + " def table_exists(self, table):\n return self.engine.dialect.has_table(self.engine, table.__name__)" + ], + [ + "STORE_NAME", + " def all_file_paths(self):\n # type: () -> List[str]\n with self.session_scope() as session:\n paths = [f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]\n paths.sort()\n if IPYTHON_FILE_PATH in paths:\n paths.remove(IPYTHON_FILE_PATH)\n paths.insert(0, IPYTHON_FILE_PATH)\n return paths" + ], + [ + "STORE_NAME", + " def clear(self):\n for model in [self.Call, self.Function, self._KeyValue]:\n if self.table_exists(model):\n model.__table__.drop(self.engine)" + ], [ "LOAD_NAME", "contextmanager" @@ -59,6 +255,14 @@ "CALL_FUNCTION", "contextmanager" ], + [ + "STORE_NAME", + " @contextmanager\n def session_scope(self):\n \"\"\"Provide a transactional scope around a series of operations.\"\"\"\n session = self.Session()\n try:\n yield session\n session.commit()\n except:\n session.rollback()\n raise\n finally:\n session.close()" + ], + [ + "STORE_NAME", + " def provide_session(self, func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)\n\n return retry_db(wrapper)" + ], [ "LOAD_FAST", "db_uri" @@ -119,6 +323,10 @@ "STORE_ATTR", "self.db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "dict" @@ -127,6 +335,10 @@ "CALL_FUNCTION_KW", "dict(\n pool_recycle=280,\n echo=False, # for convenience when debugging\n )" ], + [ + "STORE_FAST", + "kwargs" + ], [ "LOAD_GLOBAL", "create_engine" @@ -143,6 +355,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_GLOBAL", "ArgumentError" @@ -155,6 +371,10 @@ "BINARY_ADD", "'sqlite:///' + db_uri" ], + [ + "STORE_FAST", + "db_uri" + ], [ "LOAD_GLOBAL", "create_engine" @@ -171,6 +391,10 @@ "CALL_FUNCTION_EX", "create_engine(db_uri, **kwargs)" ], + [ + "STORE_FAST", + "engine" + ], [ "LOAD_FAST", "engine" @@ -207,6 +431,14 @@ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_FAST", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_GLOBAL", "declarative_base" @@ -219,18 +451,42 @@ "CALL_FUNCTION_KW", "declarative_base(cls=Base)" ], + [ + "STORE_FAST", + "Base" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_DEREF", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_FAST", "self" ], + [ + "STORE_DEREF", + "db_self" + ], [ "LOAD_GLOBAL", "object" ], + [ + "CALL_FUNCTION", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_FAST", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], [ "LOAD_FAST", "engine" @@ -251,14 +507,34 @@ "LOAD_GLOBAL", "Text" ], + [ + "STORE_DEREF", + "LongText" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_FAST", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_FAST", "Base" ], + [ + "CALL_FUNCTION", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_FAST", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_FAST", "Call" @@ -311,6 +587,10 @@ "STORE_ATTR", "self.key_value_store" ], + [ + "STORE_FAST", + "kv" + ], [ "LOAD_FAST", "_skip_version_check" @@ -415,6 +695,18 @@ "CALL_METHOD", "sys.exit('The birdseye database schema is out of date. '\n 'Run \"python -m birdseye.clear_db\" to delete the existing tables.')" ], + [ + "LOAD_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], + [ + "STORE_NAME", + " class Base(object):\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_NAME", "declared_attr" @@ -423,6 +715,10 @@ "CALL_FUNCTION", "declared_attr" ], + [ + "STORE_NAME", + " @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()" + ], [ "LOAD_FAST", "cls" @@ -439,6 +735,18 @@ "CALL_METHOD", "cls.__name__.lower()" ], + [ + "LOAD_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], + [ + "STORE_NAME", + " class KeyValue(Base):\n key = Column(String(50), primary_key=True)\n value = Column(Text)" + ], [ "LOAD_NAME", "Column" @@ -455,6 +763,10 @@ "CALL_FUNCTION_KW", "Column(String(50), primary_key=True)" ], + [ + "STORE_NAME", + "key" + ], [ "LOAD_NAME", "Column" @@ -467,14 +779,46 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "value" + ], + [ + "LOAD_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " class KeyValueStore(object):\n def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())\n\n def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))\n\n __getattr__ = __getitem__\n __setattr__ = __setitem__" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n with db_self.session_scope() as session:\n return (session\n .query(KeyValue.value)\n .filter_by(key=item)\n .scalar())" + ], + [ + "STORE_NAME", + " def __setitem__(self, key, value):\n with db_self.session_scope() as session:\n session.query(KeyValue).filter_by(key=key).delete()\n session.add(KeyValue(key=key, value=str(value)))" + ], [ "LOAD_NAME", "__getitem__" ], + [ + "STORE_NAME", + "__getattr__" + ], [ "LOAD_NAME", "__setitem__" ], + [ + "STORE_NAME", + "__setattr__" + ], [ "LOAD_DEREF", "db_self" @@ -487,6 +831,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -539,6 +887,10 @@ "CALL_METHOD", "db_self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -611,6 +963,18 @@ "CALL_METHOD", "session.add(KeyValue(key=key, value=str(value)))" ], + [ + "LOAD_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], + [ + "STORE_NAME", + " class Call(Base):\n id = Column(String(length=32), primary_key=True)\n function_id = Column(Integer, ForeignKey('function.id'), index=True)\n function = relationship('Function', backref=backref('calls', lazy='dynamic'))\n arguments = Column(Text)\n return_value = Column(Text)\n exception = Column(Text)\n traceback = Column(Text)\n data = Column(LongText)\n start_time = Column(DateTime, index=True)\n\n @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)\n\n @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))\n\n @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))\n\n @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True\n\n @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)\n\n @property\n def arguments_list(self):\n return json.loads(self.arguments)\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))\n\n basic_columns = (id, function_id, return_value,\n traceback, exception, start_time, arguments)" + ], [ "LOAD_NAME", "Column" @@ -627,6 +991,10 @@ "CALL_FUNCTION_KW", "Column(String(length=32), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -647,6 +1015,10 @@ "CALL_FUNCTION_KW", "Column(Integer, ForeignKey('function.id'), index=True)" ], + [ + "STORE_NAME", + "function_id" + ], [ "LOAD_NAME", "relationship" @@ -663,6 +1035,10 @@ "CALL_FUNCTION_KW", "relationship('Function', backref=backref('calls', lazy='dynamic'))" ], + [ + "STORE_NAME", + "function" + ], [ "LOAD_NAME", "Column" @@ -675,6 +1051,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "arguments" + ], [ "LOAD_NAME", "Column" @@ -687,6 +1067,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "return_value" + ], [ "LOAD_NAME", "Column" @@ -699,6 +1083,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "exception" + ], [ "LOAD_NAME", "Column" @@ -711,6 +1099,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "traceback" + ], [ "LOAD_NAME", "Column" @@ -723,6 +1115,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -735,6 +1131,10 @@ "CALL_FUNCTION_KW", "Column(DateTime, index=True)" ], + [ + "STORE_NAME", + "start_time" + ], [ "LOAD_NAME", "property" @@ -743,6 +1143,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def pretty_start_time(self):\n return self._pretty_time(self.start_time)" + ], [ "LOAD_NAME", "staticmethod" @@ -751,6 +1155,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _pretty_time(dt):\n if not dt:\n return ''\n return Markup('%s (%s)' % (\n dt.strftime('%Y-%m-%d %H:%M:%S'),\n naturaltime(dt)))" + ], [ "LOAD_NAME", "property" @@ -759,6 +1167,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def state_icon(self):\n return Markup('' % (\n ('ok', 'green') if self.success else\n ('remove', 'red')))" + ], [ "LOAD_NAME", "property" @@ -767,6 +1179,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def success(self):\n if self.exception:\n assert self.traceback\n assert self.return_value == 'None'\n return False\n else:\n assert not self.traceback\n return True" + ], [ "LOAD_NAME", "property" @@ -775,6 +1191,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def result(self):\n if self.success:\n return str(self.return_value)\n else:\n return str(self.exception)" + ], [ "LOAD_NAME", "property" @@ -783,6 +1203,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def arguments_list(self):\n return json.loads(self.arguments)" + ], [ "LOAD_NAME", "property" @@ -791,6 +1215,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -799,6 +1227,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(call):\n return dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" + ], [ "LOAD_NAME", "id" @@ -827,6 +1259,10 @@ "LOAD_NAME", "arguments" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_FAST", "self" @@ -1051,6 +1487,18 @@ "CALL_FUNCTION_EX", "dict(arguments=call.arguments_list,\n **select_attrs(call, 'id function_id return_value traceback '\n 'exception start_time'))" ], + [ + "LOAD_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], + [ + "STORE_NAME", + " class Function(Base):\n id = Column(Integer, Sequence('function_id_seq'), primary_key=True)\n file = Column(Text)\n name = Column(Text)\n type = Column(Text) # function or module\n html_body = Column(LongText)\n lineno = Column(Integer)\n data = Column(LongText)\n hash = Column(String(length=64), index=True)\n body_hash = Column(String(length=64), index=True)\n\n __table_args__ = (\n UniqueConstraint('hash',\n name='everything_unique'),\n Index('idx_file', 'file', mysql_length=256),\n Index('idx_name', 'name', mysql_length=32),\n )\n\n @property\n def parsed_data(self):\n return json.loads(self.data)\n\n @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')\n\n basic_columns = (file, name, lineno, hash, body_hash, type)" + ], [ "LOAD_NAME", "Column" @@ -1071,6 +1519,10 @@ "CALL_FUNCTION_KW", "Column(Integer, Sequence('function_id_seq'), primary_key=True)" ], + [ + "STORE_NAME", + "id" + ], [ "LOAD_NAME", "Column" @@ -1083,6 +1535,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "file" + ], [ "LOAD_NAME", "Column" @@ -1095,6 +1551,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "name" + ], [ "LOAD_NAME", "Column" @@ -1107,6 +1567,10 @@ "CALL_FUNCTION", "Column(Text)" ], + [ + "STORE_NAME", + "type" + ], [ "LOAD_NAME", "Column" @@ -1119,6 +1583,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "html_body" + ], [ "LOAD_NAME", "Column" @@ -1131,6 +1599,10 @@ "CALL_FUNCTION", "Column(Integer)" ], + [ + "STORE_NAME", + "lineno" + ], [ "LOAD_NAME", "Column" @@ -1143,6 +1615,10 @@ "CALL_FUNCTION", "Column(LongText)" ], + [ + "STORE_NAME", + "data" + ], [ "LOAD_NAME", "Column" @@ -1159,6 +1635,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "hash" + ], [ "LOAD_NAME", "Column" @@ -1175,6 +1655,10 @@ "CALL_FUNCTION_KW", "Column(String(length=64), index=True)" ], + [ + "STORE_NAME", + "body_hash" + ], [ "LOAD_NAME", "UniqueConstraint" @@ -1199,6 +1683,10 @@ "CALL_FUNCTION_KW", "Index('idx_name', 'name', mysql_length=32)" ], + [ + "STORE_NAME", + "__table_args__" + ], [ "LOAD_NAME", "property" @@ -1207,6 +1695,10 @@ "CALL_FUNCTION", "property" ], + [ + "STORE_NAME", + " @property\n def parsed_data(self):\n return json.loads(self.data)" + ], [ "LOAD_NAME", "staticmethod" @@ -1215,6 +1707,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def basic_dict(func):\n return select_attrs(func, 'file name lineno hash body_hash type')" + ], [ "LOAD_NAME", "file" @@ -1239,6 +1735,10 @@ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "basic_columns" + ], [ "LOAD_GLOBAL", "json" @@ -1319,6 +1819,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1351,6 +1855,14 @@ "CALL_METHOD", "session.query(self.Function.file).distinct()" ], + [ + "CALL_FUNCTION", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "paths" + ], [ "LOAD_FAST", "paths" @@ -1411,6 +1923,14 @@ "LOAD_FAST", "paths" ], + [ + "LOAD_FAST", + "[f[0] for f in session.query(self.Function.file).distinct()\n if not is_ipython_cell(f[0])]" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -1459,6 +1979,10 @@ "LOAD_ATTR", "self._KeyValue" ], + [ + "STORE_FAST", + "model" + ], [ "LOAD_FAST", "self" @@ -1511,6 +2035,10 @@ "CALL_METHOD", "self.Session()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_FAST", "session" @@ -1583,6 +2111,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args, **kwargs):\n with self.session_scope() as session:\n return func(session, *args, **kwargs)" + ], [ "LOAD_GLOBAL", "retry_db" @@ -1607,6 +2139,10 @@ "CALL_METHOD", "self.session_scope()" ], + [ + "STORE_FAST", + "session" + ], [ "LOAD_DEREF", "func" diff --git a/tests/sample_results/executing-py-3.10.json b/tests/sample_results/executing-py-3.10.json index 8afb40a..8770e3d 100644 --- a/tests/sample_results/executing-py-3.10.json +++ b/tests/sample_results/executing-py-3.10.json @@ -1,4 +1,72 @@ [ + [ + "STORE_NAME", + "\"\"\"\nGet information about what a frame is currently doing. Typical usage:\n\n import executing\n\n node = executing.Source.executing(frame).node\n # node will be an AST node or None\n\"\"\"" + ], + [ + "STORE_NAME", + "import __future__" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import dis" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + "import linecache" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from itertools import islice" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import cookie_re as encoding_pattern" + ], + [ + "STORE_NAME", + "from operator import attrgetter" + ], + [ + "STORE_NAME", + "from threading import RLock" + ], + [ + "STORE_NAME", + "__all__" + ], [ "LOAD_NAME", "sys" @@ -15,10 +83,22 @@ "COMPARE_OP", "sys.version_info[0] == 3" ], + [ + "STORE_NAME", + "PY3" + ], [ "LOAD_NAME", "PY3" ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], + [ + "STORE_NAME", + "from tokenize import detect_encoding" + ], [ "LOAD_NAME", "lru_cache" @@ -27,14 +107,34 @@ "CALL_FUNCTION_KW", "lru_cache(maxsize=None)" ], + [ + "STORE_NAME", + "cache" + ], [ "LOAD_NAME", "str" ], + [ + "STORE_NAME", + "text_type" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + " def cache(func):\n d = {}\n\n @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result\n\n return wrapper" + ], [ "LOAD_NAME", "unicode" ], + [ + "STORE_NAME", + "text_type" + ], [ "LOAD_NAME", "dis" @@ -43,6 +143,10 @@ "LOAD_ATTR", "dis.get_instructions" ], + [ + "STORE_NAME", + "get_instructions" + ], [ "LOAD_NAME", "AttributeError" @@ -55,18 +159,70 @@ "CALL_FUNCTION", "namedtuple('Instruction', 'offset argval opname')" ], + [ + "STORE_NAME", + "Instruction" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + " def get_instructions(co):\n code = co.co_code\n n = len(code)\n i = 0\n extended_arg = 0\n while i < n:\n offset = i\n c = code[i]\n op = ord(c)\n argval = None\n i = i + 1\n if op >= HAVE_ARGUMENT:\n oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg\n extended_arg = 0\n i = i + 2\n if op == EXTENDED_ARG:\n extended_arg = oparg * 65536\n\n if op in hasconst:\n argval = co.co_consts[oparg]\n yield Instruction(offset, argval, opname[op])" + ], [ "LOAD_NAME", "Exception" ], + [ + "CALL_FUNCTION", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "def only(it):\n if isinstance(it, Sized):\n if len(it) != 1:\n raise NotOneValueFound('Expected one value, found %s' % len(it))\n # noinspection PyTypeChecker\n return list(it)[0]\n\n lst = tuple(islice(it, 2))\n if len(lst) == 0:\n raise NotOneValueFound('Expected one value, found 0')\n if len(lst) > 1:\n raise NotOneValueFound('Expected one value, found several')\n return lst[0]" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_NAME", "ast" @@ -75,6 +231,14 @@ "LOAD_ATTR", "ast.NodeVisitor" ], + [ + "CALL_FUNCTION", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_NAME", "sum" @@ -87,14 +251,42 @@ "LOAD_ATTR", "__future__.all_feature_names" ], + [ + "CALL_FUNCTION", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], [ "CALL_FUNCTION", "sum(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" ], + [ + "STORE_NAME", + "future_flags" + ], + [ + "STORE_NAME", + "def compile_similar_to(source, matching_code):\n return compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" + ], + [ + "STORE_NAME", + "sentinel" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "def get_setter(node):\n parent = node.parent\n for name, field in ast.iter_fields(parent):\n if field is node:\n return lambda new_node: setattr(parent, name, new_node)\n elif isinstance(field, list):\n for i, item in enumerate(field):\n if item is node:\n def setter(new_node):\n field[i] = new_node\n\n return setter" + ], [ "LOAD_NAME", "RLock" @@ -103,6 +295,18 @@ "CALL_FUNCTION", "RLock()" ], + [ + "STORE_NAME", + "lock" + ], + [ + "STORE_NAME", + "def find_codes(root_code, matching):\n def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )\n\n code_options = []\n if matches(root_code):\n code_options.append(root_code)\n\n def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)\n\n finder(root_code)\n return code_options" + ], + [ + "STORE_NAME", + "def code_names(code):\n return frozenset().union(\n code.co_names,\n code.co_varnames,\n code.co_freevars,\n code.co_cellvars,\n )" + ], [ "LOAD_NAME", "cache" @@ -111,6 +315,14 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + "@cache\ndef statement_containing_node(node):\n while not isinstance(node, ast.stmt):\n node = node.parent\n return node" + ], + [ + "STORE_DEREF", + "d" + ], [ "LOAD_GLOBAL", "functools" @@ -131,6 +343,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result" + ], [ "LOAD_FAST", "wrapper" @@ -171,6 +387,10 @@ "CALL_FUNCTION_EX", "func(*args)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "d" @@ -195,6 +415,10 @@ "LOAD_ATTR", "co.co_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "len" @@ -207,6 +431,18 @@ "CALL_FUNCTION", "len(code)" ], + [ + "STORE_FAST", + "n" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -223,6 +459,10 @@ "LOAD_FAST", "i" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "code" @@ -235,6 +475,10 @@ "BINARY_SUBSCR", "code[i]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -247,6 +491,14 @@ "CALL_FUNCTION", "ord(c)" ], + [ + "STORE_FAST", + "op" + ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_FAST", "i" @@ -255,6 +507,10 @@ "BINARY_ADD", "i + 1" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -327,6 +583,14 @@ "BINARY_ADD", "ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg" ], + [ + "STORE_FAST", + "oparg" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -335,6 +599,10 @@ "BINARY_ADD", "i + 2" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -355,6 +623,10 @@ "BINARY_MULTIPLY", "oparg * 65536" ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "op" @@ -383,6 +655,10 @@ "BINARY_SUBSCR", "co.co_consts[oparg]" ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_GLOBAL", "Instruction" @@ -423,6 +699,18 @@ "COMPARE_OP", "i < n" ], + [ + "LOAD_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], [ "LOAD_GLOBAL", "isinstance" @@ -515,6 +803,10 @@ "CALL_FUNCTION", "tuple(islice(it, 2))" ], + [ + "STORE_FAST", + "lst" + ], [ "LOAD_GLOBAL", "len" @@ -571,6 +863,26 @@ "BINARY_SUBSCR", "lst[0]" ], + [ + "LOAD_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "\"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames" + ], [ "LOAD_NAME", "classmethod" @@ -579,6 +891,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})" + ], [ "LOAD_NAME", "classmethod" @@ -587,6 +903,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -595,6 +915,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)" + ], [ "LOAD_NAME", "classmethod" @@ -603,6 +927,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)" + ], [ "LOAD_NAME", "classmethod" @@ -611,6 +939,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result" + ], [ "LOAD_NAME", "cache" @@ -619,6 +951,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], [ "LOAD_NAME", "cache" @@ -627,6 +963,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )" + ], [ "LOAD_NAME", "staticmethod" @@ -635,6 +975,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source" + ], + [ + "STORE_NAME", + " def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_FAST", "filename" @@ -679,6 +1027,10 @@ "CALL_METHOD", "self.decode_source(text)" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "text" @@ -699,6 +1051,10 @@ "LOAD_FAST", "text" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_METHOD", "''.join" @@ -723,10 +1079,18 @@ "CALL_FUNCTION", "enumerate(text.splitlines(True))" ], + [ + "CALL_FUNCTION", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], [ "CALL_METHOD", "''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_GLOBAL", "defaultdict" @@ -819,6 +1183,10 @@ "CALL_METHOD", "ast.walk(self.tree)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -835,6 +1203,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -899,6 +1271,10 @@ "CALL_FUNCTION", "QualnameVisitor()" ], + [ + "STORE_FAST", + "visitor" + ], [ "LOAD_FAST", "visitor" @@ -935,6 +1311,18 @@ "STORE_ATTR", "self._qualnames" ], + [ + "LOAD_FAST", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "i" @@ -1007,6 +1395,10 @@ "CALL_METHOD", "cls._class_local('__source_cache', {})" ], + [ + "STORE_FAST", + "source_cache" + ], [ "LOAD_FAST", "source_cache" @@ -1043,6 +1435,10 @@ "CALL_METHOD", "linecache.getlines(filename, module_globals)" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "cls" @@ -1067,6 +1463,10 @@ "CALL_FUNCTION", "cls(filename, ''.join(lines))" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "source_cache" @@ -1143,6 +1543,10 @@ "LOAD_ATTR", "frame.f_lasti" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "cls" @@ -1155,6 +1559,10 @@ "CALL_METHOD", "cls._class_local('__executing_cache', {})" ], + [ + "STORE_FAST", + "executing_cache" + ], [ "LOAD_FAST", "executing_cache" @@ -1167,6 +1575,10 @@ "BINARY_SUBSCR", "executing_cache[key]" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1187,6 +1599,18 @@ "CALL_METHOD", "cls.for_frame(frame)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1215,6 +1639,10 @@ "CALL_METHOD", "source.statements_at_line(frame.f_lineno)" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_GLOBAL", "NodeFinder" @@ -1243,6 +1671,10 @@ "LOAD_ATTR", "NodeFinder(frame, stmts, source.tree).result" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "Exception" @@ -1259,6 +1691,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "new_stmts" + ], [ "LOAD_FAST", "new_stmts" @@ -1275,6 +1711,10 @@ "LOAD_FAST", "new_stmts" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1287,6 +1727,10 @@ "LOAD_FAST", "stmts" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "args" @@ -1343,6 +1787,10 @@ "CALL_METHOD", "cls.__dict__.get(name, default)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "setattr" @@ -1383,6 +1831,18 @@ "BINARY_SUBSCR", "self._nodes_by_line[lineno]" ], + [ + "CALL_FUNCTION", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "LOAD_FAST", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "statement_containing_node" @@ -1395,6 +1855,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "from asttokens import ASTTokens" + ], [ "LOAD_FAST", "ASTTokens" @@ -1471,6 +1935,14 @@ "CALL_FUNCTION", "detect_encoding(io.BytesIO(source).readline)" ], + [ + "STORE_FAST", + "encoding" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_FAST", "source" @@ -1487,6 +1959,10 @@ "CALL_METHOD", "source.decode(encoding)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "source" @@ -1551,6 +2027,38 @@ "CALL_METHOD", "self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" ], + [ + "LOAD_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "\"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts" + ], + [ + "STORE_NAME", + " def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)" + ], + [ + "STORE_NAME", + " def text(self):\n return self.source.asttokens().get_text(self.node)" + ], + [ + "STORE_NAME", + " def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_FAST", "frame" @@ -1691,6 +2199,34 @@ "CALL_METHOD", "self.source.asttokens().get_text_range(self.node)" ], + [ + "LOAD_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + " def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}" + ], + [ + "STORE_NAME", + " def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)" + ], + [ + "STORE_NAME", + " def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')" + ], + [ + "STORE_NAME", + " def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_GLOBAL", "super" @@ -1743,6 +2279,10 @@ "LOAD_ATTR", "node.name" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "self" @@ -1851,6 +2391,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "node" @@ -1859,10 +2403,18 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "children" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "self" @@ -1927,6 +2479,14 @@ "CALL_METHOD", "ast.iter_fields(node)" ], + [ + "STORE_FAST", + "field" + ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "field" @@ -1991,6 +2551,10 @@ "LOAD_FAST", "child" ], + [ + "STORE_FAST", + "grandchild" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2099,6 +2663,14 @@ "CALL_METHOD", "self.stack.pop()" ], + [ + "LOAD_FAST", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], + [ + "STORE_FAST", + "fname" + ], [ "LOAD_GLOBAL", "getattr" @@ -2155,6 +2727,30 @@ "CALL_FUNCTION_KW", "compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" ], + [ + "LOAD_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + " def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))" + ], + [ + "STORE_NAME", + " def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr" + ], + [ + "STORE_NAME", + " def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], [ "LOAD_FAST", "frame" @@ -2203,6 +2799,10 @@ "BINARY_SUBSCR", "frame.f_code.co_code[frame.f_lasti]" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "PY3" @@ -2219,6 +2819,10 @@ "CALL_FUNCTION", "ord(b)" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "dis" @@ -2235,6 +2839,10 @@ "BINARY_SUBSCR", "dis.opname[b]" ], + [ + "STORE_FAST", + "op_name" + ], [ "LOAD_FAST", "op_name" @@ -2255,6 +2863,10 @@ "LOAD_ATTR", "ast.Call" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2271,6 +2883,10 @@ "LOAD_ATTR", "ast.Subscript" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2291,6 +2907,10 @@ "LOAD_ATTR", "ast.BinOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2311,6 +2931,10 @@ "LOAD_ATTR", "ast.UnaryOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2327,6 +2951,10 @@ "LOAD_ATTR", "ast.Attribute" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2343,6 +2971,10 @@ "LOAD_ATTR", "ast.Compare" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_GLOBAL", "RuntimeError" @@ -2363,6 +2995,14 @@ "LOAD_FAST", "stmts" ], + [ + "CALL_FUNCTION", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "exprs" + ], [ "LOAD_GLOBAL", "only" @@ -2403,6 +3043,18 @@ "STORE_ATTR", "self.result" ], + [ + "CALL_FUNCTION", + " with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))" + ], + [ + "LOAD_FAST", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "ast" @@ -2419,6 +3071,10 @@ "CALL_METHOD", "ast.walk(stmt)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2487,6 +3143,14 @@ "CALL_FUNCTION", "enumerate(exprs)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "expr" + ], [ "LOAD_GLOBAL", "get_setter" @@ -2499,6 +3163,10 @@ "CALL_FUNCTION", "get_setter(expr)" ], + [ + "STORE_FAST", + "setter" + ], [ "LOAD_GLOBAL", "ast" @@ -2543,6 +3211,10 @@ "CALL_FUNCTION_KW", "ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )" ], + [ + "STORE_FAST", + "replacement" + ], [ "LOAD_GLOBAL", "ast" @@ -2583,6 +3255,10 @@ "CALL_METHOD", "self.compile_instructions()" ], + [ + "STORE_FAST", + "instructions" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -2635,6 +3311,14 @@ "CALL_FUNCTION", "enumerate(instructions)" ], + [ + "CALL_FUNCTION", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_FAST", "indices" @@ -2655,6 +3339,10 @@ "BINARY_SUBTRACT", "only(indices) - 1" ], + [ + "STORE_FAST", + "arg_index" + ], [ "LOAD_FAST", "instructions" @@ -2675,6 +3363,14 @@ "COMPARE_OP", "instructions[arg_index].opname == 'EXTENDED_ARG'" ], + [ + "LOAD_FAST", + "arg_index" + ], + [ + "STORE_FAST", + "arg_index" + ], [ "LOAD_FAST", "instructions" @@ -2731,6 +3427,18 @@ "LOAD_FAST", "expr" ], + [ + "LOAD_FAST", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "instruction" + ], [ "LOAD_FAST", "instruction" @@ -2779,6 +3487,10 @@ "CALL_FUNCTION", "compile_similar_to(self.tree, self.frame.f_code)" ], + [ + "STORE_FAST", + "module_code" + ], [ "LOAD_GLOBAL", "only" @@ -2811,6 +3523,10 @@ "CALL_FUNCTION", "only(find_codes(module_code, self.frame.f_code))" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "list" @@ -2839,6 +3555,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_DEREF", + "parent" + ], [ "LOAD_GLOBAL", "ast" @@ -2855,6 +3575,14 @@ "CALL_METHOD", "ast.iter_fields(parent)" ], + [ + "STORE_DEREF", + "name" + ], + [ + "STORE_DEREF", + "field" + ], [ "LOAD_DEREF", "field" @@ -2895,6 +3623,14 @@ "CALL_FUNCTION", "enumerate(field)" ], + [ + "STORE_DEREF", + "i" + ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_FAST", "item" @@ -2907,6 +3643,10 @@ "IS_OP", "item is node" ], + [ + "STORE_FAST", + " def setter(new_node):\n field[i] = new_node" + ], [ "LOAD_FAST", "setter" @@ -2947,6 +3687,14 @@ "STORE_SUBSCR", "field[i]" ], + [ + "STORE_DEREF", + " def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_DEREF", + "code_options" + ], [ "LOAD_DEREF", "matches" @@ -2975,6 +3723,10 @@ "CALL_METHOD", "code_options.append(root_code)" ], + [ + "STORE_DEREF", + " def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)" + ], [ "LOAD_DEREF", "finder" @@ -3015,10 +3767,22 @@ "LOAD_GLOBAL", "code_names" ], + [ + "CALL_FUNCTION", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], [ "CALL_FUNCTION", "all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" ], + [ + "LOAD_FAST", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -3055,6 +3819,10 @@ "LOAD_ATTR", "code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -3187,6 +3955,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" diff --git a/tests/sample_results/executing-py-3.8.json b/tests/sample_results/executing-py-3.8.json index d7fd6d2..34b757c 100644 --- a/tests/sample_results/executing-py-3.8.json +++ b/tests/sample_results/executing-py-3.8.json @@ -1,4 +1,72 @@ [ + [ + "STORE_NAME", + "\"\"\"\nGet information about what a frame is currently doing. Typical usage:\n\n import executing\n\n node = executing.Source.executing(frame).node\n # node will be an AST node or None\n\"\"\"" + ], + [ + "STORE_NAME", + "import __future__" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import dis" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + "import linecache" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from itertools import islice" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import cookie_re as encoding_pattern" + ], + [ + "STORE_NAME", + "from operator import attrgetter" + ], + [ + "STORE_NAME", + "from threading import RLock" + ], + [ + "STORE_NAME", + "__all__" + ], [ "LOAD_NAME", "sys" @@ -15,10 +83,22 @@ "COMPARE_OP", "sys.version_info[0] == 3" ], + [ + "STORE_NAME", + "PY3" + ], [ "LOAD_NAME", "PY3" ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], + [ + "STORE_NAME", + "from tokenize import detect_encoding" + ], [ "LOAD_NAME", "lru_cache" @@ -27,14 +107,34 @@ "CALL_FUNCTION_KW", "lru_cache(maxsize=None)" ], + [ + "STORE_NAME", + "cache" + ], [ "LOAD_NAME", "str" ], + [ + "STORE_NAME", + "text_type" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + " def cache(func):\n d = {}\n\n @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result\n\n return wrapper" + ], [ "LOAD_NAME", "unicode" ], + [ + "STORE_NAME", + "text_type" + ], [ "LOAD_NAME", "dis" @@ -43,6 +143,10 @@ "LOAD_ATTR", "dis.get_instructions" ], + [ + "STORE_NAME", + "get_instructions" + ], [ "LOAD_NAME", "AttributeError" @@ -55,18 +159,70 @@ "CALL_FUNCTION", "namedtuple('Instruction', 'offset argval opname')" ], + [ + "STORE_NAME", + "Instruction" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + " def get_instructions(co):\n code = co.co_code\n n = len(code)\n i = 0\n extended_arg = 0\n while i < n:\n offset = i\n c = code[i]\n op = ord(c)\n argval = None\n i = i + 1\n if op >= HAVE_ARGUMENT:\n oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg\n extended_arg = 0\n i = i + 2\n if op == EXTENDED_ARG:\n extended_arg = oparg * 65536\n\n if op in hasconst:\n argval = co.co_consts[oparg]\n yield Instruction(offset, argval, opname[op])" + ], [ "LOAD_NAME", "Exception" ], + [ + "CALL_FUNCTION", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "def only(it):\n if isinstance(it, Sized):\n if len(it) != 1:\n raise NotOneValueFound('Expected one value, found %s' % len(it))\n # noinspection PyTypeChecker\n return list(it)[0]\n\n lst = tuple(islice(it, 2))\n if len(lst) == 0:\n raise NotOneValueFound('Expected one value, found 0')\n if len(lst) > 1:\n raise NotOneValueFound('Expected one value, found several')\n return lst[0]" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_NAME", "ast" @@ -75,6 +231,14 @@ "LOAD_ATTR", "ast.NodeVisitor" ], + [ + "CALL_FUNCTION", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_NAME", "sum" @@ -87,14 +251,42 @@ "LOAD_ATTR", "__future__.all_feature_names" ], + [ + "CALL_FUNCTION", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], [ "CALL_FUNCTION", "sum(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" ], + [ + "STORE_NAME", + "future_flags" + ], + [ + "STORE_NAME", + "def compile_similar_to(source, matching_code):\n return compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" + ], + [ + "STORE_NAME", + "sentinel" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "def get_setter(node):\n parent = node.parent\n for name, field in ast.iter_fields(parent):\n if field is node:\n return lambda new_node: setattr(parent, name, new_node)\n elif isinstance(field, list):\n for i, item in enumerate(field):\n if item is node:\n def setter(new_node):\n field[i] = new_node\n\n return setter" + ], [ "LOAD_NAME", "RLock" @@ -103,6 +295,18 @@ "CALL_FUNCTION", "RLock()" ], + [ + "STORE_NAME", + "lock" + ], + [ + "STORE_NAME", + "def find_codes(root_code, matching):\n def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )\n\n code_options = []\n if matches(root_code):\n code_options.append(root_code)\n\n def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)\n\n finder(root_code)\n return code_options" + ], + [ + "STORE_NAME", + "def code_names(code):\n return frozenset().union(\n code.co_names,\n code.co_varnames,\n code.co_freevars,\n code.co_cellvars,\n )" + ], [ "LOAD_NAME", "cache" @@ -111,6 +315,14 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + "@cache\ndef statement_containing_node(node):\n while not isinstance(node, ast.stmt):\n node = node.parent\n return node" + ], + [ + "STORE_DEREF", + "d" + ], [ "LOAD_GLOBAL", "functools" @@ -131,6 +343,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result" + ], [ "LOAD_FAST", "wrapper" @@ -171,6 +387,10 @@ "CALL_FUNCTION_EX", "func(*args)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "d" @@ -195,6 +415,10 @@ "LOAD_ATTR", "co.co_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "len" @@ -207,6 +431,18 @@ "CALL_FUNCTION", "len(code)" ], + [ + "STORE_FAST", + "n" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -223,6 +459,10 @@ "LOAD_FAST", "i" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "code" @@ -235,6 +475,10 @@ "BINARY_SUBSCR", "code[i]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -247,6 +491,14 @@ "CALL_FUNCTION", "ord(c)" ], + [ + "STORE_FAST", + "op" + ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_FAST", "i" @@ -255,6 +507,10 @@ "BINARY_ADD", "i + 1" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -327,6 +583,14 @@ "BINARY_ADD", "ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg" ], + [ + "STORE_FAST", + "oparg" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -335,6 +599,10 @@ "BINARY_ADD", "i + 2" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -355,6 +623,10 @@ "BINARY_MULTIPLY", "oparg * 65536" ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "op" @@ -383,6 +655,10 @@ "BINARY_SUBSCR", "co.co_consts[oparg]" ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_GLOBAL", "Instruction" @@ -411,6 +687,18 @@ "CALL_FUNCTION", "Instruction(offset, argval, opname[op])" ], + [ + "LOAD_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], [ "LOAD_GLOBAL", "isinstance" @@ -503,6 +791,10 @@ "CALL_FUNCTION", "tuple(islice(it, 2))" ], + [ + "STORE_FAST", + "lst" + ], [ "LOAD_GLOBAL", "len" @@ -559,6 +851,26 @@ "BINARY_SUBSCR", "lst[0]" ], + [ + "LOAD_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "\"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames" + ], [ "LOAD_NAME", "classmethod" @@ -567,6 +879,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})" + ], [ "LOAD_NAME", "classmethod" @@ -575,6 +891,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -583,6 +903,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)" + ], [ "LOAD_NAME", "classmethod" @@ -591,6 +915,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)" + ], [ "LOAD_NAME", "classmethod" @@ -599,6 +927,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result" + ], [ "LOAD_NAME", "cache" @@ -607,6 +939,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], [ "LOAD_NAME", "cache" @@ -615,6 +951,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )" + ], [ "LOAD_NAME", "staticmethod" @@ -623,6 +963,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source" + ], + [ + "STORE_NAME", + " def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_FAST", "filename" @@ -667,6 +1015,10 @@ "CALL_METHOD", "self.decode_source(text)" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "text" @@ -687,6 +1039,10 @@ "LOAD_FAST", "text" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_METHOD", "''.join" @@ -711,10 +1067,18 @@ "CALL_FUNCTION", "enumerate(text.splitlines(True))" ], + [ + "CALL_FUNCTION", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], [ "CALL_METHOD", "''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_GLOBAL", "defaultdict" @@ -807,6 +1171,10 @@ "CALL_METHOD", "ast.walk(self.tree)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -823,6 +1191,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -887,6 +1259,10 @@ "CALL_FUNCTION", "QualnameVisitor()" ], + [ + "STORE_FAST", + "visitor" + ], [ "LOAD_FAST", "visitor" @@ -923,6 +1299,18 @@ "STORE_ATTR", "self._qualnames" ], + [ + "LOAD_FAST", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "i" @@ -995,6 +1383,10 @@ "CALL_METHOD", "cls._class_local('__source_cache', {})" ], + [ + "STORE_FAST", + "source_cache" + ], [ "LOAD_FAST", "source_cache" @@ -1031,6 +1423,10 @@ "CALL_METHOD", "linecache.getlines(filename, module_globals)" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "cls" @@ -1055,6 +1451,10 @@ "CALL_FUNCTION", "cls(filename, ''.join(lines))" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "source_cache" @@ -1131,6 +1531,10 @@ "LOAD_ATTR", "frame.f_lasti" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "cls" @@ -1143,6 +1547,10 @@ "CALL_METHOD", "cls._class_local('__executing_cache', {})" ], + [ + "STORE_FAST", + "executing_cache" + ], [ "LOAD_FAST", "executing_cache" @@ -1155,6 +1563,10 @@ "BINARY_SUBSCR", "executing_cache[key]" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1175,6 +1587,18 @@ "CALL_METHOD", "cls.for_frame(frame)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1203,6 +1627,10 @@ "CALL_METHOD", "source.statements_at_line(frame.f_lineno)" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_GLOBAL", "NodeFinder" @@ -1231,6 +1659,10 @@ "LOAD_ATTR", "NodeFinder(frame, stmts, source.tree).result" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "Exception" @@ -1247,6 +1679,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "new_stmts" + ], [ "LOAD_FAST", "new_stmts" @@ -1263,6 +1699,10 @@ "LOAD_FAST", "new_stmts" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1275,6 +1715,10 @@ "LOAD_FAST", "stmts" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "args" @@ -1331,6 +1775,10 @@ "CALL_METHOD", "cls.__dict__.get(name, default)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "setattr" @@ -1371,6 +1819,18 @@ "BINARY_SUBSCR", "self._nodes_by_line[lineno]" ], + [ + "CALL_FUNCTION", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "LOAD_FAST", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "statement_containing_node" @@ -1383,6 +1843,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "from asttokens import ASTTokens" + ], [ "LOAD_FAST", "ASTTokens" @@ -1459,6 +1923,14 @@ "CALL_FUNCTION", "detect_encoding(io.BytesIO(source).readline)" ], + [ + "STORE_FAST", + "encoding" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_FAST", "source" @@ -1475,6 +1947,10 @@ "CALL_METHOD", "source.decode(encoding)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "source" @@ -1539,6 +2015,38 @@ "CALL_METHOD", "self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" ], + [ + "LOAD_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "\"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts" + ], + [ + "STORE_NAME", + " def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)" + ], + [ + "STORE_NAME", + " def text(self):\n return self.source.asttokens().get_text(self.node)" + ], + [ + "STORE_NAME", + " def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_FAST", "frame" @@ -1679,6 +2187,34 @@ "CALL_METHOD", "self.source.asttokens().get_text_range(self.node)" ], + [ + "LOAD_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + " def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}" + ], + [ + "STORE_NAME", + " def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)" + ], + [ + "STORE_NAME", + " def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')" + ], + [ + "STORE_NAME", + " def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_GLOBAL", "super" @@ -1731,6 +2267,10 @@ "LOAD_ATTR", "node.name" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "self" @@ -1839,6 +2379,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "node" @@ -1847,10 +2391,18 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "children" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "self" @@ -1915,6 +2467,14 @@ "CALL_METHOD", "ast.iter_fields(node)" ], + [ + "STORE_FAST", + "field" + ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "field" @@ -1979,6 +2539,10 @@ "LOAD_FAST", "child" ], + [ + "STORE_FAST", + "grandchild" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2087,6 +2651,14 @@ "CALL_METHOD", "self.stack.pop()" ], + [ + "LOAD_FAST", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], + [ + "STORE_FAST", + "fname" + ], [ "LOAD_GLOBAL", "getattr" @@ -2143,6 +2715,30 @@ "CALL_FUNCTION_KW", "compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" ], + [ + "LOAD_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + " def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))" + ], + [ + "STORE_NAME", + " def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr" + ], + [ + "STORE_NAME", + " def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], [ "LOAD_FAST", "frame" @@ -2191,6 +2787,10 @@ "BINARY_SUBSCR", "frame.f_code.co_code[frame.f_lasti]" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "PY3" @@ -2207,6 +2807,10 @@ "CALL_FUNCTION", "ord(b)" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "dis" @@ -2223,6 +2827,10 @@ "BINARY_SUBSCR", "dis.opname[b]" ], + [ + "STORE_FAST", + "op_name" + ], [ "LOAD_FAST", "op_name" @@ -2243,6 +2851,10 @@ "LOAD_ATTR", "ast.Call" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2259,6 +2871,10 @@ "LOAD_ATTR", "ast.Subscript" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2279,6 +2895,10 @@ "LOAD_ATTR", "ast.BinOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2299,6 +2919,10 @@ "LOAD_ATTR", "ast.UnaryOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2315,6 +2939,10 @@ "LOAD_ATTR", "ast.Attribute" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2331,6 +2959,10 @@ "LOAD_ATTR", "ast.Compare" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_GLOBAL", "RuntimeError" @@ -2351,6 +2983,14 @@ "LOAD_FAST", "stmts" ], + [ + "CALL_FUNCTION", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "exprs" + ], [ "LOAD_GLOBAL", "only" @@ -2391,6 +3031,14 @@ "STORE_ATTR", "self.result" ], + [ + "LOAD_FAST", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "ast" @@ -2407,6 +3055,10 @@ "CALL_METHOD", "ast.walk(stmt)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2475,6 +3127,14 @@ "CALL_FUNCTION", "enumerate(exprs)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "expr" + ], [ "LOAD_GLOBAL", "get_setter" @@ -2487,6 +3147,10 @@ "CALL_FUNCTION", "get_setter(expr)" ], + [ + "STORE_FAST", + "setter" + ], [ "LOAD_GLOBAL", "ast" @@ -2531,6 +3195,10 @@ "CALL_FUNCTION_KW", "ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )" ], + [ + "STORE_FAST", + "replacement" + ], [ "LOAD_GLOBAL", "ast" @@ -2571,6 +3239,10 @@ "CALL_METHOD", "self.compile_instructions()" ], + [ + "STORE_FAST", + "instructions" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -2599,6 +3271,14 @@ "CALL_FUNCTION", "enumerate(instructions)" ], + [ + "CALL_FUNCTION", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_FAST", "indices" @@ -2619,6 +3299,10 @@ "BINARY_SUBTRACT", "only(indices) - 1" ], + [ + "STORE_FAST", + "arg_index" + ], [ "LOAD_FAST", "instructions" @@ -2639,6 +3323,14 @@ "COMPARE_OP", "instructions[arg_index].opname == 'EXTENDED_ARG'" ], + [ + "LOAD_FAST", + "arg_index -= 1" + ], + [ + "STORE_FAST", + "arg_index -= 1" + ], [ "LOAD_FAST", "instructions" @@ -2675,6 +3367,18 @@ "LOAD_FAST", "expr" ], + [ + "LOAD_FAST", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "instruction" + ], [ "LOAD_FAST", "instruction" @@ -2723,6 +3427,10 @@ "CALL_FUNCTION", "compile_similar_to(self.tree, self.frame.f_code)" ], + [ + "STORE_FAST", + "module_code" + ], [ "LOAD_GLOBAL", "only" @@ -2755,6 +3463,10 @@ "CALL_FUNCTION", "only(find_codes(module_code, self.frame.f_code))" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "list" @@ -2783,6 +3495,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_DEREF", + "parent" + ], [ "LOAD_GLOBAL", "ast" @@ -2799,6 +3515,14 @@ "CALL_METHOD", "ast.iter_fields(parent)" ], + [ + "STORE_DEREF", + "name" + ], + [ + "STORE_DEREF", + "field" + ], [ "LOAD_DEREF", "field" @@ -2839,6 +3563,14 @@ "CALL_FUNCTION", "enumerate(field)" ], + [ + "STORE_DEREF", + "i" + ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_FAST", "item" @@ -2851,6 +3583,10 @@ "COMPARE_OP", "item is node" ], + [ + "STORE_FAST", + " def setter(new_node):\n field[i] = new_node" + ], [ "LOAD_FAST", "setter" @@ -2891,6 +3627,14 @@ "STORE_SUBSCR", "field[i]" ], + [ + "STORE_DEREF", + " def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_DEREF", + "code_options" + ], [ "LOAD_DEREF", "matches" @@ -2919,6 +3663,10 @@ "CALL_METHOD", "code_options.append(root_code)" ], + [ + "STORE_DEREF", + " def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)" + ], [ "LOAD_DEREF", "finder" @@ -2959,10 +3707,22 @@ "LOAD_GLOBAL", "code_names" ], + [ + "CALL_FUNCTION", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], [ "CALL_FUNCTION", "all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" ], + [ + "LOAD_FAST", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -2999,6 +3759,10 @@ "LOAD_ATTR", "code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -3131,6 +3895,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" diff --git a/tests/sample_results/executing-py-3.9.json b/tests/sample_results/executing-py-3.9.json index 49fdaf7..5a19b96 100644 --- a/tests/sample_results/executing-py-3.9.json +++ b/tests/sample_results/executing-py-3.9.json @@ -1,4 +1,72 @@ [ + [ + "STORE_NAME", + "\"\"\"\nGet information about what a frame is currently doing. Typical usage:\n\n import executing\n\n node = executing.Source.executing(frame).node\n # node will be an AST node or None\n\"\"\"" + ], + [ + "STORE_NAME", + "import __future__" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import dis" + ], + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + "import linecache" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from collections import defaultdict, namedtuple, Sized" + ], + [ + "STORE_NAME", + "from itertools import islice" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import cookie_re as encoding_pattern" + ], + [ + "STORE_NAME", + "from operator import attrgetter" + ], + [ + "STORE_NAME", + "from threading import RLock" + ], + [ + "STORE_NAME", + "__all__" + ], [ "LOAD_NAME", "sys" @@ -15,10 +83,22 @@ "COMPARE_OP", "sys.version_info[0] == 3" ], + [ + "STORE_NAME", + "PY3" + ], [ "LOAD_NAME", "PY3" ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], + [ + "STORE_NAME", + "from tokenize import detect_encoding" + ], [ "LOAD_NAME", "lru_cache" @@ -27,14 +107,34 @@ "CALL_FUNCTION_KW", "lru_cache(maxsize=None)" ], + [ + "STORE_NAME", + "cache" + ], [ "LOAD_NAME", "str" ], + [ + "STORE_NAME", + "text_type" + ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + " def cache(func):\n d = {}\n\n @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result\n\n return wrapper" + ], [ "LOAD_NAME", "unicode" ], + [ + "STORE_NAME", + "text_type" + ], [ "LOAD_NAME", "dis" @@ -43,6 +143,10 @@ "LOAD_ATTR", "dis.get_instructions" ], + [ + "STORE_NAME", + "get_instructions" + ], [ "LOAD_NAME", "AttributeError" @@ -55,18 +159,70 @@ "CALL_FUNCTION", "namedtuple('Instruction', 'offset argval opname')" ], + [ + "STORE_NAME", + "Instruction" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + "from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname" + ], + [ + "STORE_NAME", + " def get_instructions(co):\n code = co.co_code\n n = len(code)\n i = 0\n extended_arg = 0\n while i < n:\n offset = i\n c = code[i]\n op = ord(c)\n argval = None\n i = i + 1\n if op >= HAVE_ARGUMENT:\n oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg\n extended_arg = 0\n i = i + 2\n if op == EXTENDED_ARG:\n extended_arg = oparg * 65536\n\n if op in hasconst:\n argval = co.co_consts[oparg]\n yield Instruction(offset, argval, opname[op])" + ], [ "LOAD_NAME", "Exception" ], + [ + "CALL_FUNCTION", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "def only(it):\n if isinstance(it, Sized):\n if len(it) != 1:\n raise NotOneValueFound('Expected one value, found %s' % len(it))\n # noinspection PyTypeChecker\n return list(it)[0]\n\n lst = tuple(islice(it, 2))\n if len(lst) == 0:\n raise NotOneValueFound('Expected one value, found 0')\n if len(lst) > 1:\n raise NotOneValueFound('Expected one value, found several')\n return lst[0]" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_NAME", "ast" @@ -75,6 +231,14 @@ "LOAD_ATTR", "ast.NodeVisitor" ], + [ + "CALL_FUNCTION", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_NAME", "sum" @@ -87,14 +251,42 @@ "LOAD_ATTR", "__future__.all_feature_names" ], + [ + "CALL_FUNCTION", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], [ "CALL_FUNCTION", "sum(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" ], + [ + "STORE_NAME", + "future_flags" + ], + [ + "STORE_NAME", + "def compile_similar_to(source, matching_code):\n return compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" + ], + [ + "STORE_NAME", + "sentinel" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "def get_setter(node):\n parent = node.parent\n for name, field in ast.iter_fields(parent):\n if field is node:\n return lambda new_node: setattr(parent, name, new_node)\n elif isinstance(field, list):\n for i, item in enumerate(field):\n if item is node:\n def setter(new_node):\n field[i] = new_node\n\n return setter" + ], [ "LOAD_NAME", "RLock" @@ -103,6 +295,18 @@ "CALL_FUNCTION", "RLock()" ], + [ + "STORE_NAME", + "lock" + ], + [ + "STORE_NAME", + "def find_codes(root_code, matching):\n def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )\n\n code_options = []\n if matches(root_code):\n code_options.append(root_code)\n\n def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)\n\n finder(root_code)\n return code_options" + ], + [ + "STORE_NAME", + "def code_names(code):\n return frozenset().union(\n code.co_names,\n code.co_varnames,\n code.co_freevars,\n code.co_cellvars,\n )" + ], [ "LOAD_NAME", "cache" @@ -111,6 +315,14 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + "@cache\ndef statement_containing_node(node):\n while not isinstance(node, ast.stmt):\n node = node.parent\n return node" + ], + [ + "STORE_DEREF", + "d" + ], [ "LOAD_GLOBAL", "functools" @@ -131,6 +343,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*args):\n if args in d:\n return d[args]\n result = d[args] = func(*args)\n return result" + ], [ "LOAD_FAST", "wrapper" @@ -171,6 +387,10 @@ "CALL_FUNCTION_EX", "func(*args)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "d" @@ -195,6 +415,10 @@ "LOAD_ATTR", "co.co_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "len" @@ -207,6 +431,18 @@ "CALL_FUNCTION", "len(code)" ], + [ + "STORE_FAST", + "n" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -223,6 +459,10 @@ "LOAD_FAST", "i" ], + [ + "STORE_FAST", + "offset" + ], [ "LOAD_FAST", "code" @@ -235,6 +475,10 @@ "BINARY_SUBSCR", "code[i]" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -247,6 +491,14 @@ "CALL_FUNCTION", "ord(c)" ], + [ + "STORE_FAST", + "op" + ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_FAST", "i" @@ -255,6 +507,10 @@ "BINARY_ADD", "i + 1" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -327,6 +583,14 @@ "BINARY_ADD", "ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg" ], + [ + "STORE_FAST", + "oparg" + ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "i" @@ -335,6 +599,10 @@ "BINARY_ADD", "i + 2" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "op" @@ -355,6 +623,10 @@ "BINARY_MULTIPLY", "oparg * 65536" ], + [ + "STORE_FAST", + "extended_arg" + ], [ "LOAD_FAST", "op" @@ -383,6 +655,10 @@ "BINARY_SUBSCR", "co.co_consts[oparg]" ], + [ + "STORE_FAST", + "argval" + ], [ "LOAD_GLOBAL", "Instruction" @@ -411,6 +687,18 @@ "CALL_FUNCTION", "Instruction(offset, argval, opname[op])" ], + [ + "LOAD_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], + [ + "STORE_NAME", + "class NotOneValueFound(Exception):\n pass" + ], [ "LOAD_GLOBAL", "isinstance" @@ -503,6 +791,10 @@ "CALL_FUNCTION", "tuple(islice(it, 2))" ], + [ + "STORE_FAST", + "lst" + ], [ "LOAD_GLOBAL", "len" @@ -559,6 +851,26 @@ "BINARY_SUBSCR", "lst[0]" ], + [ + "LOAD_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "class Source(object):\n \"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"\n\n def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames\n\n @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})\n\n @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result\n\n @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)\n\n @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)\n\n @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result\n\n @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }\n\n @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )\n\n @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source\n\n def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], + [ + "STORE_NAME", + "\"\"\"\n The source code of a single file and associated metadata.\n\n The main method of interest is the classmethod `executing(frame)`.\n\n If you want an instance of this class, don't construct it.\n Ideally use the classmethod `for_frame(frame)`.\n If you don't have a frame, use `for_filename(filename [, module_globals])`.\n These methods cache instances by filename, so at most one instance exists per filename.\n\n Attributes:\n - filename\n - text\n - tree: AST parsed from text, or None if text is not valid Python\n All nodes in the tree have an extra `parent` attribute\n\n Other methods of interest:\n - statements_at_line\n - asttokens\n - code_qualname\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, filename, text):\n \"\"\"\n Don't call this constructor, see the class docstring.\n \"\"\"\n\n self.filename = filename\n\n if not isinstance(text, text_type):\n text = self.decode_source(text)\n self.text = text\n\n if PY3:\n ast_text = text\n else:\n # In python 2 it's a syntax error to parse unicode\n # with an encoding declaration, so we remove it but\n # leave empty lines in its place to keep line numbers the same\n ast_text = ''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])\n\n self._nodes_by_line = defaultdict(list)\n self.tree = None\n self._qualnames = {}\n\n if text:\n try:\n self.tree = ast.parse(ast_text, filename=filename)\n except SyntaxError:\n pass\n else:\n for node in ast.walk(self.tree):\n for child in ast.iter_child_nodes(node):\n child.parent = node\n if hasattr(node, 'lineno'):\n self._nodes_by_line[node.lineno].append(node)\n\n visitor = QualnameVisitor()\n visitor.visit(self.tree)\n self._qualnames = visitor.qualnames" + ], [ "LOAD_NAME", "classmethod" @@ -567,6 +879,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_frame(cls, frame):\n \"\"\"\n Returns the `Source` object corresponding to the file the frame is executing in.\n \"\"\"\n return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {})" + ], [ "LOAD_NAME", "classmethod" @@ -575,6 +891,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def for_filename(cls, filename, module_globals=None):\n source_cache = cls._class_local('__source_cache', {})\n try:\n return source_cache[filename]\n except KeyError:\n pass\n\n lines = linecache.getlines(filename, module_globals)\n result = source_cache[filename] = cls(filename, ''.join(lines))\n return result" + ], [ "LOAD_NAME", "classmethod" @@ -583,6 +903,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def lazycache(cls, frame):\n if hasattr(linecache, 'lazycache'):\n linecache.lazycache(frame.f_code.co_filename, frame.f_globals)" + ], [ "LOAD_NAME", "classmethod" @@ -591,6 +915,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def executing(cls, frame):\n \"\"\"\n Returns an `Executing` object representing the operation\n currently executing in the given frame.\n \"\"\"\n key = (frame.f_code, frame.f_lasti)\n executing_cache = cls._class_local('__executing_cache', {})\n\n try:\n args = executing_cache[key]\n except KeyError:\n source = cls.for_frame(frame)\n node = stmts = None\n if source.tree:\n stmts = source.statements_at_line(frame.f_lineno)\n try:\n node = NodeFinder(frame, stmts, source.tree).result\n except Exception:\n raise\n else:\n new_stmts = {statement_containing_node(node)}\n assert new_stmts <= stmts\n stmts = new_stmts\n\n args = source, node, stmts\n executing_cache[key] = args\n\n return Executing(frame, *args)" + ], [ "LOAD_NAME", "classmethod" @@ -599,6 +927,10 @@ "CALL_FUNCTION", "classmethod" ], + [ + "STORE_NAME", + " @classmethod\n def _class_local(cls, name, default):\n \"\"\"\n Returns an attribute directly associated with this class\n (as opposed to subclasses), setting default if necessary\n \"\"\"\n # classes have a mappingproxy preventing us from using setdefault\n result = cls.__dict__.get(name, default)\n setattr(cls, name, result)\n return result" + ], [ "LOAD_NAME", "cache" @@ -607,6 +939,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def statements_at_line(self, lineno):\n \"\"\"\n Returns the statement nodes overlapping the given line.\n\n Returns at most one statement unless semicolons are present.\n\n If the `text` attribute is not valid python, meaning\n `tree` is None, returns an empty set.\n\n Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`\n should return at least one statement.\n \"\"\"\n\n return {\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], [ "LOAD_NAME", "cache" @@ -615,6 +951,10 @@ "CALL_FUNCTION", "cache" ], + [ + "STORE_NAME", + " @cache\n def asttokens(self):\n \"\"\"\n Returns an ASTTokens object for getting the source of specific AST nodes.\n\n See http://asttokens.readthedocs.io/en/latest/api-index.html\n \"\"\"\n from asttokens import ASTTokens # must be installed separately\n return ASTTokens(\n self.text,\n tree=self.tree,\n filename=self.filename,\n )" + ], [ "LOAD_NAME", "staticmethod" @@ -623,6 +963,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def decode_source(source):\n if isinstance(source, bytes):\n encoding, _ = detect_encoding(io.BytesIO(source).readline)\n source = source.decode(encoding)\n return source" + ], + [ + "STORE_NAME", + " def code_qualname(self, code):\n \"\"\"\n Imitates the __qualname__ attribute of functions for code objects.\n Given:\n\n - A function `func`\n - A frame `frame` for an execution of `func`, meaning:\n `frame.f_code is func.__code__`\n\n `Source.for_frame(frame).code_qualname(frame.f_code)`\n will be equal to `func.__qualname__`*. Works for Python 2 as well,\n where of course no `__qualname__` attribute exists.\n\n Falls back to `code.co_name` if there is no appropriate qualname.\n\n Based on https://github.com/wbolster/qualname\n\n (* unless `func` is a lambda\n nested inside another lambda on the same line, in which case\n the outer lambda's qualname will be returned for the codes\n of both lambdas)\n \"\"\"\n assert code.co_filename == self.filename\n return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" + ], [ "LOAD_FAST", "filename" @@ -667,6 +1015,10 @@ "CALL_METHOD", "self.decode_source(text)" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "text" @@ -687,6 +1039,10 @@ "LOAD_FAST", "text" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_METHOD", "''.join" @@ -711,10 +1067,18 @@ "CALL_FUNCTION", "enumerate(text.splitlines(True))" ], + [ + "CALL_FUNCTION", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], [ "CALL_METHOD", "''.join([\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ])" ], + [ + "STORE_FAST", + "ast_text" + ], [ "LOAD_GLOBAL", "defaultdict" @@ -807,6 +1171,10 @@ "CALL_METHOD", "ast.walk(self.tree)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -823,6 +1191,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -887,6 +1259,10 @@ "CALL_FUNCTION", "QualnameVisitor()" ], + [ + "STORE_FAST", + "visitor" + ], [ "LOAD_FAST", "visitor" @@ -923,6 +1299,18 @@ "STORE_ATTR", "self._qualnames" ], + [ + "LOAD_FAST", + "[\n '\\n' if i < 2 and encoding_pattern.match(line)\n else line\n for i, line in enumerate(text.splitlines(True))\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "i" @@ -995,6 +1383,10 @@ "CALL_METHOD", "cls._class_local('__source_cache', {})" ], + [ + "STORE_FAST", + "source_cache" + ], [ "LOAD_FAST", "source_cache" @@ -1031,6 +1423,10 @@ "CALL_METHOD", "linecache.getlines(filename, module_globals)" ], + [ + "STORE_FAST", + "lines" + ], [ "LOAD_FAST", "cls" @@ -1055,6 +1451,10 @@ "CALL_FUNCTION", "cls(filename, ''.join(lines))" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "source_cache" @@ -1131,6 +1531,10 @@ "LOAD_ATTR", "frame.f_lasti" ], + [ + "STORE_FAST", + "key" + ], [ "LOAD_FAST", "cls" @@ -1143,6 +1547,10 @@ "CALL_METHOD", "cls._class_local('__executing_cache', {})" ], + [ + "STORE_FAST", + "executing_cache" + ], [ "LOAD_FAST", "executing_cache" @@ -1155,6 +1563,10 @@ "BINARY_SUBSCR", "executing_cache[key]" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "KeyError" @@ -1175,6 +1587,18 @@ "CALL_METHOD", "cls.for_frame(frame)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "node" + ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1203,6 +1627,10 @@ "CALL_METHOD", "source.statements_at_line(frame.f_lineno)" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_GLOBAL", "NodeFinder" @@ -1231,6 +1659,10 @@ "LOAD_ATTR", "NodeFinder(frame, stmts, source.tree).result" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "Exception" @@ -1247,6 +1679,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "new_stmts" + ], [ "LOAD_FAST", "new_stmts" @@ -1263,6 +1699,10 @@ "LOAD_FAST", "new_stmts" ], + [ + "STORE_FAST", + "stmts" + ], [ "LOAD_FAST", "source" @@ -1275,6 +1715,10 @@ "LOAD_FAST", "stmts" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_FAST", "args" @@ -1331,6 +1775,10 @@ "CALL_METHOD", "cls.__dict__.get(name, default)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "setattr" @@ -1371,6 +1819,18 @@ "BINARY_SUBSCR", "self._nodes_by_line[lineno]" ], + [ + "CALL_FUNCTION", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "LOAD_FAST", + "{\n statement_containing_node(node)\n for node in\n self._nodes_by_line[lineno]\n }" + ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "statement_containing_node" @@ -1383,6 +1843,10 @@ "CALL_FUNCTION", "statement_containing_node(node)" ], + [ + "STORE_FAST", + "from asttokens import ASTTokens" + ], [ "LOAD_FAST", "ASTTokens" @@ -1459,6 +1923,14 @@ "CALL_FUNCTION", "detect_encoding(io.BytesIO(source).readline)" ], + [ + "STORE_FAST", + "encoding" + ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_FAST", "source" @@ -1475,6 +1947,10 @@ "CALL_METHOD", "source.decode(encoding)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "source" @@ -1539,6 +2015,38 @@ "CALL_METHOD", "self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)" ], + [ + "LOAD_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "class Executing(object):\n \"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"\n\n def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts\n\n def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)\n\n def text(self):\n return self.source.asttokens().get_text(self.node)\n\n def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], + [ + "STORE_NAME", + "\"\"\"\n Information about the operation a frame is currently executing.\n\n Generally you will just want `node`, which is the AST node being executed,\n or None if it's unknown.\n Currently `node` can only be an `ast.Call` object, other operations\n will be supported in future.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self, frame, source, node, stmts):\n self.frame = frame\n self.source = source\n self.node = node\n self.statements = stmts" + ], + [ + "STORE_NAME", + " def code_qualname(self):\n return self.source.code_qualname(self.frame.f_code)" + ], + [ + "STORE_NAME", + " def text(self):\n return self.source.asttokens().get_text(self.node)" + ], + [ + "STORE_NAME", + " def text_range(self):\n return self.source.asttokens().get_text_range(self.node)" + ], [ "LOAD_FAST", "frame" @@ -1679,6 +2187,34 @@ "CALL_METHOD", "self.source.asttokens().get_text_range(self.node)" ], + [ + "LOAD_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + "class QualnameVisitor(ast.NodeVisitor):\n def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}\n\n def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)\n\n def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')\n\n def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], + [ + "STORE_NAME", + " def __init__(self):\n super(QualnameVisitor, self).__init__()\n self.stack = []\n self.qualnames = {}" + ], + [ + "STORE_NAME", + " def visit_FunctionDef(self, node, name=None):\n name = name or node.name\n self.stack.append(name)\n self.qualnames.setdefault((name, node.lineno), \".\".join(self.stack))\n\n self.stack.append('')\n if isinstance(node, ast.Lambda):\n children = [node.body]\n else:\n children = node.body\n for child in children:\n self.visit(child)\n self.stack.pop()\n self.stack.pop()\n\n # Find lambdas in the function definition outside the body,\n # e.g. decorators or default arguments\n # Based on iter_child_nodes\n for field, child in ast.iter_fields(node):\n if field == 'body':\n continue\n if isinstance(child, ast.AST):\n self.visit(child)\n elif isinstance(child, list):\n for grandchild in child:\n if isinstance(grandchild, ast.AST):\n self.visit(grandchild)" + ], + [ + "STORE_NAME", + " def visit_Lambda(self, node):\n self.visit_FunctionDef(node, '')" + ], + [ + "STORE_NAME", + " def visit_ClassDef(self, node):\n self.stack.append(node.name)\n self.generic_visit(node)\n self.stack.pop()" + ], [ "LOAD_GLOBAL", "super" @@ -1731,6 +2267,10 @@ "LOAD_ATTR", "node.name" ], + [ + "STORE_FAST", + "name" + ], [ "LOAD_FAST", "self" @@ -1839,6 +2379,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "node" @@ -1847,10 +2391,18 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "children" + ], [ "LOAD_FAST", "children" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "self" @@ -1915,6 +2467,14 @@ "CALL_METHOD", "ast.iter_fields(node)" ], + [ + "STORE_FAST", + "field" + ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "field" @@ -1979,6 +2539,10 @@ "LOAD_FAST", "child" ], + [ + "STORE_FAST", + "grandchild" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2087,6 +2651,14 @@ "CALL_METHOD", "self.stack.pop()" ], + [ + "LOAD_FAST", + "(\n getattr(__future__, fname).compiler_flag\n for fname in __future__.all_feature_names\n)" + ], + [ + "STORE_FAST", + "fname" + ], [ "LOAD_GLOBAL", "getattr" @@ -2143,6 +2715,30 @@ "CALL_FUNCTION_KW", "compile(\n source,\n matching_code.co_filename,\n 'exec',\n flags=future_flags & matching_code.co_flags,\n dont_inherit=True,\n )" ], + [ + "LOAD_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + "class NodeFinder(object):\n def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))\n\n def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr\n\n def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], + [ + "STORE_NAME", + " def __init__(self, frame, stmts, tree):\n self.frame = frame\n self.tree = tree\n\n b = frame.f_code.co_code[frame.f_lasti]\n if not PY3:\n b = ord(b)\n op_name = dis.opname[b]\n\n if op_name.startswith('CALL_'):\n typ = ast.Call\n elif op_name == 'BINARY_SUBSCR':\n typ = ast.Subscript\n elif op_name.startswith('BINARY_'):\n typ = ast.BinOp\n elif op_name.startswith('UNARY_'):\n typ = ast.UnaryOp\n elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):\n typ = ast.Attribute\n elif op_name == 'COMPARE_OP':\n typ = ast.Compare\n else:\n raise RuntimeError(op_name)\n\n with lock:\n exprs = {\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }\n\n self.result = only(list(self.matching_nodes(exprs)))" + ], + [ + "STORE_NAME", + " def matching_nodes(self, exprs):\n for i, expr in enumerate(exprs):\n setter = get_setter(expr)\n replacement = ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )\n ast.fix_missing_locations(replacement)\n setter(replacement)\n try:\n instructions = self.compile_instructions()\n except SyntaxError:\n continue\n finally:\n setter(expr)\n indices = [\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]\n if not indices:\n continue\n arg_index = only(indices) - 1\n while instructions[arg_index].opname == 'EXTENDED_ARG':\n arg_index -= 1\n\n if instructions[arg_index].offset == self.frame.f_lasti:\n yield expr" + ], + [ + "STORE_NAME", + " def compile_instructions(self):\n module_code = compile_similar_to(self.tree, self.frame.f_code)\n code = only(find_codes(module_code, self.frame.f_code))\n return list(get_instructions(code))" + ], [ "LOAD_FAST", "frame" @@ -2191,6 +2787,10 @@ "BINARY_SUBSCR", "frame.f_code.co_code[frame.f_lasti]" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "PY3" @@ -2207,6 +2807,10 @@ "CALL_FUNCTION", "ord(b)" ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "dis" @@ -2223,6 +2827,10 @@ "BINARY_SUBSCR", "dis.opname[b]" ], + [ + "STORE_FAST", + "op_name" + ], [ "LOAD_FAST", "op_name" @@ -2243,6 +2851,10 @@ "LOAD_ATTR", "ast.Call" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2259,6 +2871,10 @@ "LOAD_ATTR", "ast.Subscript" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2279,6 +2895,10 @@ "LOAD_ATTR", "ast.BinOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2299,6 +2919,10 @@ "LOAD_ATTR", "ast.UnaryOp" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2315,6 +2939,10 @@ "LOAD_ATTR", "ast.Attribute" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_FAST", "op_name" @@ -2331,6 +2959,10 @@ "LOAD_ATTR", "ast.Compare" ], + [ + "STORE_DEREF", + "typ" + ], [ "LOAD_GLOBAL", "RuntimeError" @@ -2351,6 +2983,14 @@ "LOAD_FAST", "stmts" ], + [ + "CALL_FUNCTION", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "exprs" + ], [ "LOAD_GLOBAL", "only" @@ -2391,6 +3031,14 @@ "STORE_ATTR", "self.result" ], + [ + "LOAD_FAST", + "{\n node\n for stmt in stmts\n for node in ast.walk(stmt)\n if isinstance(node, typ)\n if not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load))\n }" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "ast" @@ -2407,6 +3055,10 @@ "CALL_METHOD", "ast.walk(stmt)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -2475,6 +3127,14 @@ "CALL_FUNCTION", "enumerate(exprs)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "expr" + ], [ "LOAD_GLOBAL", "get_setter" @@ -2487,6 +3147,10 @@ "CALL_FUNCTION", "get_setter(expr)" ], + [ + "STORE_FAST", + "setter" + ], [ "LOAD_GLOBAL", "ast" @@ -2531,6 +3195,10 @@ "CALL_FUNCTION_KW", "ast.BinOp(\n left=expr,\n op=ast.Pow(),\n right=ast.Str(s=sentinel),\n )" ], + [ + "STORE_FAST", + "replacement" + ], [ "LOAD_GLOBAL", "ast" @@ -2571,6 +3239,10 @@ "CALL_METHOD", "self.compile_instructions()" ], + [ + "STORE_FAST", + "instructions" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -2623,6 +3295,14 @@ "CALL_FUNCTION", "enumerate(instructions)" ], + [ + "CALL_FUNCTION", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "indices" + ], [ "LOAD_FAST", "indices" @@ -2643,6 +3323,10 @@ "BINARY_SUBTRACT", "only(indices) - 1" ], + [ + "STORE_FAST", + "arg_index" + ], [ "LOAD_FAST", "instructions" @@ -2663,6 +3347,14 @@ "COMPARE_OP", "instructions[arg_index].opname == 'EXTENDED_ARG'" ], + [ + "LOAD_FAST", + "arg_index" + ], + [ + "STORE_FAST", + "arg_index" + ], [ "LOAD_FAST", "instructions" @@ -2699,6 +3391,18 @@ "LOAD_FAST", "expr" ], + [ + "LOAD_FAST", + "[\n i\n for i, instruction in enumerate(instructions)\n if instruction.argval == sentinel\n ]" + ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "instruction" + ], [ "LOAD_FAST", "instruction" @@ -2747,6 +3451,10 @@ "CALL_FUNCTION", "compile_similar_to(self.tree, self.frame.f_code)" ], + [ + "STORE_FAST", + "module_code" + ], [ "LOAD_GLOBAL", "only" @@ -2779,6 +3487,10 @@ "CALL_FUNCTION", "only(find_codes(module_code, self.frame.f_code))" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "list" @@ -2807,6 +3519,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_DEREF", + "parent" + ], [ "LOAD_GLOBAL", "ast" @@ -2823,6 +3539,14 @@ "CALL_METHOD", "ast.iter_fields(parent)" ], + [ + "STORE_DEREF", + "name" + ], + [ + "STORE_DEREF", + "field" + ], [ "LOAD_DEREF", "field" @@ -2863,6 +3587,14 @@ "CALL_FUNCTION", "enumerate(field)" ], + [ + "STORE_DEREF", + "i" + ], + [ + "STORE_FAST", + "item" + ], [ "LOAD_FAST", "item" @@ -2875,6 +3607,10 @@ "IS_OP", "item is node" ], + [ + "STORE_FAST", + " def setter(new_node):\n field[i] = new_node" + ], [ "LOAD_FAST", "setter" @@ -2915,6 +3651,14 @@ "STORE_SUBSCR", "field[i]" ], + [ + "STORE_DEREF", + " def matches(c):\n return all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_DEREF", + "code_options" + ], [ "LOAD_DEREF", "matches" @@ -2943,6 +3687,10 @@ "CALL_METHOD", "code_options.append(root_code)" ], + [ + "STORE_DEREF", + " def finder(code):\n for const in code.co_consts:\n if not inspect.iscode(const):\n continue\n\n if matches(const):\n code_options.append(const)\n finder(const)" + ], [ "LOAD_DEREF", "finder" @@ -2983,10 +3731,22 @@ "LOAD_GLOBAL", "code_names" ], + [ + "CALL_FUNCTION", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], [ "CALL_FUNCTION", "all(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" ], + [ + "LOAD_FAST", + "(\n f(c) == f(matching)\n for f in [\n attrgetter('co_firstlineno'),\n attrgetter('co_name'),\n code_names,\n ]\n )" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -3023,6 +3783,10 @@ "LOAD_ATTR", "code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -3155,6 +3919,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" diff --git a/tests/sample_results/import_hook-py-3.10.json b/tests/sample_results/import_hook-py-3.10.json index f4941fd..ea92d70 100644 --- a/tests/sample_results/import_hook-py-3.10.json +++ b/tests/sample_results/import_hook-py-3.10.json @@ -1,8 +1,76 @@ [ + [ + "STORE_NAME", + "import logging" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from importlib.util import spec_from_loader" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "CALL_FUNCTION", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "def should_trace(source):\n trace_stmt = None\n deep = False\n for stmt in ast.parse(source).body:\n if isinstance(stmt, ast.Import):\n for alias in stmt.names:\n if alias.name.startswith('birdseye.trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n\n if isinstance(stmt, ast.ImportFrom) and stmt.module == 'birdseye':\n for alias in stmt.names:\n if alias.name.startswith('trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n return deep, trace_stmt" + ], + [ + "LOAD_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + " def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep" + ], + [ + "STORE_NAME", + " def create_module(self, spec):\n pass" + ], + [ + "STORE_NAME", + " def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )" + ], + [ + "STORE_NAME", + " def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)" + ], + [ + "STORE_NAME", + " def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_FAST", "spec" @@ -39,6 +107,10 @@ "STORE_ATTR", "self.deep" ], + [ + "STORE_FAST", + "from birdseye.bird import eye" + ], [ "LOAD_FAST", "eye" @@ -143,6 +215,34 @@ "CALL_METHOD", "self._spec.loader.is_package(fullname)" ], + [ + "LOAD_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "\"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"" + ], + [ + "STORE_NAME", + " def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec" + ], + [ + "STORE_NAME", + " def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "sys" @@ -151,6 +251,10 @@ "LOAD_ATTR", "sys.meta_path" ], + [ + "STORE_FAST", + "finder" + ], [ "LOAD_FAST", "finder" @@ -211,6 +315,10 @@ "CALL_FUNCTION_KW", "finder.find_spec(fullname, path, target=target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "hasattr" @@ -239,6 +347,10 @@ "CALL_FUNCTION", "spec_from_loader(fullname, finder)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -287,6 +399,10 @@ "CALL_METHOD", "self._find_plain_spec(fullname, path, target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -375,6 +491,10 @@ "CALL_METHOD", "spec.loader.get_source(fullname)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "ImportError" @@ -439,6 +559,14 @@ "CALL_FUNCTION", "should_trace(source)" ], + [ + "STORE_FAST", + "deep" + ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "trace_stmt" @@ -463,6 +591,10 @@ "CALL_FUNCTION", "BirdsEyeLoader(spec, source, deep)" ], + [ + "STORE_FAST", + "loader" + ], [ "LOAD_GLOBAL", "spec_from_loader" @@ -479,6 +611,14 @@ "CALL_FUNCTION", "spec_from_loader(fullname, loader)" ], + [ + "STORE_FAST", + "trace_stmt" + ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +639,10 @@ "LOAD_ATTR", "ast.parse(source).body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "isinstance" @@ -527,6 +671,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -547,6 +695,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -563,6 +715,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "isinstance" @@ -603,6 +759,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -623,6 +783,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -639,6 +803,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_FAST", "deep" diff --git a/tests/sample_results/import_hook-py-3.8.json b/tests/sample_results/import_hook-py-3.8.json index a2f5276..0cfd63d 100644 --- a/tests/sample_results/import_hook-py-3.8.json +++ b/tests/sample_results/import_hook-py-3.8.json @@ -1,8 +1,76 @@ [ + [ + "STORE_NAME", + "import logging" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from importlib.util import spec_from_loader" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "CALL_FUNCTION", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "def should_trace(source):\n trace_stmt = None\n deep = False\n for stmt in ast.parse(source).body:\n if isinstance(stmt, ast.Import):\n for alias in stmt.names:\n if alias.name.startswith('birdseye.trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n\n if isinstance(stmt, ast.ImportFrom) and stmt.module == 'birdseye':\n for alias in stmt.names:\n if alias.name.startswith('trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n return deep, trace_stmt" + ], + [ + "LOAD_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + " def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep" + ], + [ + "STORE_NAME", + " def create_module(self, spec):\n pass" + ], + [ + "STORE_NAME", + " def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )" + ], + [ + "STORE_NAME", + " def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)" + ], + [ + "STORE_NAME", + " def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_FAST", "spec" @@ -39,6 +107,10 @@ "STORE_ATTR", "self.deep" ], + [ + "STORE_FAST", + "from birdseye.bird import eye" + ], [ "LOAD_FAST", "eye" @@ -143,6 +215,34 @@ "CALL_METHOD", "self._spec.loader.is_package(fullname)" ], + [ + "LOAD_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "\"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"" + ], + [ + "STORE_NAME", + " def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec" + ], + [ + "STORE_NAME", + " def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "sys" @@ -151,6 +251,10 @@ "LOAD_ATTR", "sys.meta_path" ], + [ + "STORE_FAST", + "finder" + ], [ "LOAD_FAST", "finder" @@ -211,6 +315,10 @@ "CALL_FUNCTION_KW", "finder.find_spec(fullname, path, target=target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "hasattr" @@ -239,6 +347,10 @@ "CALL_FUNCTION", "spec_from_loader(fullname, finder)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -287,6 +399,10 @@ "CALL_METHOD", "self._find_plain_spec(fullname, path, target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -375,6 +491,10 @@ "CALL_METHOD", "spec.loader.get_source(fullname)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "ImportError" @@ -439,6 +559,14 @@ "CALL_FUNCTION", "should_trace(source)" ], + [ + "STORE_FAST", + "deep" + ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "trace_stmt" @@ -463,6 +591,10 @@ "CALL_FUNCTION", "BirdsEyeLoader(spec, source, deep)" ], + [ + "STORE_FAST", + "loader" + ], [ "LOAD_GLOBAL", "spec_from_loader" @@ -479,6 +611,14 @@ "CALL_FUNCTION", "spec_from_loader(fullname, loader)" ], + [ + "STORE_FAST", + "trace_stmt" + ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +639,10 @@ "LOAD_ATTR", "ast.parse(source).body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "isinstance" @@ -527,6 +671,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -547,6 +695,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -563,6 +715,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "isinstance" @@ -603,6 +759,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -623,6 +783,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -639,6 +803,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_FAST", "deep" diff --git a/tests/sample_results/import_hook-py-3.9.json b/tests/sample_results/import_hook-py-3.9.json index f4941fd..ea92d70 100644 --- a/tests/sample_results/import_hook-py-3.9.json +++ b/tests/sample_results/import_hook-py-3.9.json @@ -1,8 +1,76 @@ [ + [ + "STORE_NAME", + "import logging" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from importlib.util import spec_from_loader" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "CALL_FUNCTION", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "def should_trace(source):\n trace_stmt = None\n deep = False\n for stmt in ast.parse(source).body:\n if isinstance(stmt, ast.Import):\n for alias in stmt.names:\n if alias.name.startswith('birdseye.trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n\n if isinstance(stmt, ast.ImportFrom) and stmt.module == 'birdseye':\n for alias in stmt.names:\n if alias.name.startswith('trace_module'):\n trace_stmt = stmt\n if alias.name.endswith('deep'):\n deep = True\n return deep, trace_stmt" + ], + [ + "LOAD_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + "class BirdsEyeLoader:\n\n def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep\n\n def create_module(self, spec):\n pass\n\n def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )\n\n def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)\n\n def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], + [ + "STORE_NAME", + " def __init__(self, spec, source, deep):\n self._spec = spec\n self.source = source\n self.deep = deep" + ], + [ + "STORE_NAME", + " def create_module(self, spec):\n pass" + ], + [ + "STORE_NAME", + " def exec_module(self, module):\n from birdseye.bird import eye\n eye.exec_string(\n source=self.source,\n filename=self._spec.origin,\n globs=module.__dict__,\n locs=module.__dict__,\n deep=self.deep,\n )" + ], + [ + "STORE_NAME", + " def get_filename(self, fullname):\n return self._spec.loader.get_filename(fullname)" + ], + [ + "STORE_NAME", + " def is_package(self, fullname):\n return self._spec.loader.is_package(fullname)" + ], [ "LOAD_FAST", "spec" @@ -39,6 +107,10 @@ "STORE_ATTR", "self.deep" ], + [ + "STORE_FAST", + "from birdseye.bird import eye" + ], [ "LOAD_FAST", "eye" @@ -143,6 +215,34 @@ "CALL_METHOD", "self._spec.loader.is_package(fullname)" ], + [ + "LOAD_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "class BirdsEyeFinder(object):\n \"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"\n\n def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec\n\n def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_NAME", + "\"\"\"Loads a module and looks for tracing inside, only providing a loader\n if it finds some.\n \"\"\"" + ], + [ + "STORE_NAME", + " def _find_plain_spec(self, fullname, path, target):\n \"\"\"Try to find the original module using all the\n remaining meta_path finders.\"\"\"\n spec = None\n for finder in sys.meta_path:\n # when testing with pytest, it installs a finder that for\n # some yet unknown reasons makes birdseye\n # fail. For now it will just avoid using it and pass to\n # the next one\n if finder is self or 'pytest' in finder.__module__:\n continue\n if hasattr(finder, 'find_spec'):\n spec = finder.find_spec(fullname, path, target=target)\n elif hasattr(finder, 'load_module'):\n spec = spec_from_loader(fullname, finder)\n\n if spec is not None and spec.origin != 'builtin':\n return spec" + ], + [ + "STORE_NAME", + " def find_spec(self, fullname, path, target=None):\n spec = self._find_plain_spec(fullname, path, target)\n if spec is None or not (hasattr(spec.loader, 'get_source') and\n callable(spec.loader.get_source)): # noqa: E128\n if fullname != 'org':\n # stdlib pickle.py at line 94 contains a ``from\n # org.python.core for Jython which is always failing,\n # of course\n logging.debug('Failed finding spec for %s', fullname)\n return\n\n try:\n source = spec.loader.get_source(fullname)\n except ImportError:\n logging.debug('Loader for %s was unable to find the sources',\n fullname)\n return\n except Exception:\n logging.exception('Loader for %s raised an error', fullname)\n return\n\n if not source or 'birdseye' not in source:\n return\n\n deep, trace_stmt = should_trace(source)\n\n if not trace_stmt:\n return\n\n loader = BirdsEyeLoader(spec, source, deep)\n return spec_from_loader(fullname, loader)" + ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "sys" @@ -151,6 +251,10 @@ "LOAD_ATTR", "sys.meta_path" ], + [ + "STORE_FAST", + "finder" + ], [ "LOAD_FAST", "finder" @@ -211,6 +315,10 @@ "CALL_FUNCTION_KW", "finder.find_spec(fullname, path, target=target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_GLOBAL", "hasattr" @@ -239,6 +347,10 @@ "CALL_FUNCTION", "spec_from_loader(fullname, finder)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -287,6 +399,10 @@ "CALL_METHOD", "self._find_plain_spec(fullname, path, target)" ], + [ + "STORE_FAST", + "spec" + ], [ "LOAD_FAST", "spec" @@ -375,6 +491,10 @@ "CALL_METHOD", "spec.loader.get_source(fullname)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "ImportError" @@ -439,6 +559,14 @@ "CALL_FUNCTION", "should_trace(source)" ], + [ + "STORE_FAST", + "deep" + ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "trace_stmt" @@ -463,6 +591,10 @@ "CALL_FUNCTION", "BirdsEyeLoader(spec, source, deep)" ], + [ + "STORE_FAST", + "loader" + ], [ "LOAD_GLOBAL", "spec_from_loader" @@ -479,6 +611,14 @@ "CALL_FUNCTION", "spec_from_loader(fullname, loader)" ], + [ + "STORE_FAST", + "trace_stmt" + ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +639,10 @@ "LOAD_ATTR", "ast.parse(source).body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "isinstance" @@ -527,6 +671,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -547,6 +695,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -563,6 +715,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_GLOBAL", "isinstance" @@ -603,6 +759,10 @@ "LOAD_ATTR", "stmt.names" ], + [ + "STORE_FAST", + "alias" + ], [ "LOAD_FAST", "alias" @@ -623,6 +783,10 @@ "LOAD_FAST", "stmt" ], + [ + "STORE_FAST", + "trace_stmt" + ], [ "LOAD_FAST", "alias" @@ -639,6 +803,10 @@ "CALL_METHOD", "alias.name.endswith('deep')" ], + [ + "STORE_FAST", + "deep" + ], [ "LOAD_FAST", "deep" diff --git a/tests/sample_results/ipython-py-3.10.json b/tests/sample_results/ipython-py-3.10.json index e5a987c..1324265 100644 --- a/tests/sample_results/ipython-py-3.10.json +++ b/tests/sample_results/ipython-py-3.10.json @@ -1,4 +1,104 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import socket" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from werkzeug.local import LocalProxy" + ], + [ + "STORE_NAME", + "from werkzeug.serving import ThreadingMixIn" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], [ "LOAD_NAME", "PY2" @@ -11,6 +111,18 @@ "LOAD_NAME", "StringIO" ], + [ + "STORE_NAME", + "fake_stream" + ], + [ + "STORE_NAME", + "thread_proxies" + ], + [ + "STORE_NAME", + "def stream_proxy(original):\n def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)\n\n return LocalProxy(p)" + ], [ "LOAD_NAME", "stream_proxy" @@ -59,6 +171,10 @@ "STORE_ATTR", "sys.stdout" ], + [ + "STORE_NAME", + "def run_server(port, bind_host, show_server_output):\n if not show_server_output:\n thread_proxies[current_thread().ident] = fake_stream()\n try:\n server.app.run(\n debug=True,\n port=port,\n host=bind_host,\n use_reloader=False,\n )\n except socket.error:\n pass" + ], [ "LOAD_NAME", "Environment" @@ -83,6 +199,10 @@ "CALL_FUNCTION_KW", "Environment(\n loader=PackageLoader('birdseye', 'templates'),\n autoescape=select_autoescape(['html', 'xml'])\n)" ], + [ + "STORE_NAME", + "templates_env" + ], [ "LOAD_NAME", "magics_class" @@ -91,10 +211,22 @@ "LOAD_NAME", "Magics" ], + [ + "CALL_FUNCTION", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "CALL_FUNCTION", "magics_class" ], + [ + "STORE_NAME", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], + [ + "STORE_FAST", + " def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)" + ], [ "LOAD_GLOBAL", "LocalProxy" @@ -119,6 +251,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -163,6 +299,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -267,6 +407,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )" ], + [ + "STORE_NAME", + "server_url" + ], [ "LOAD_NAME", "Int" @@ -275,6 +419,10 @@ "CALL_FUNCTION_KW", "Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "port" + ], [ "LOAD_NAME", "Unicode" @@ -283,6 +431,10 @@ "CALL_FUNCTION_KW", "Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )" ], + [ + "STORE_NAME", + "bind_host" + ], [ "LOAD_NAME", "Bool" @@ -291,6 +443,10 @@ "CALL_FUNCTION_KW", "Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "show_server_output" + ], [ "LOAD_NAME", "Unicode" @@ -299,6 +455,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )" ], + [ + "STORE_NAME", + "db_url" + ], [ "LOAD_NAME", "cell_magic" @@ -307,6 +467,10 @@ "CALL_FUNCTION", "cell_magic" ], + [ + "STORE_NAME", + " @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "LOAD_DEREF", "self" @@ -467,6 +631,10 @@ "STORE_ATTR", "eye.db" ], + [ + "STORE_FAST", + " def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)" + ], [ "LOAD_GLOBAL", "eye" @@ -487,6 +655,10 @@ "CALL_METHOD", "eye.exec_ipython_cell(cell, callback)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -567,6 +739,10 @@ "CALL_FUNCTION", "HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))" ], + [ + "STORE_FAST", + "html" + ], [ "LOAD_GLOBAL", "display" diff --git a/tests/sample_results/ipython-py-3.8.json b/tests/sample_results/ipython-py-3.8.json index bd84ce7..219dd29 100644 --- a/tests/sample_results/ipython-py-3.8.json +++ b/tests/sample_results/ipython-py-3.8.json @@ -1,4 +1,104 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import socket" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from werkzeug.local import LocalProxy" + ], + [ + "STORE_NAME", + "from werkzeug.serving import ThreadingMixIn" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], [ "LOAD_NAME", "PY2" @@ -11,6 +111,18 @@ "LOAD_NAME", "StringIO" ], + [ + "STORE_NAME", + "fake_stream" + ], + [ + "STORE_NAME", + "thread_proxies" + ], + [ + "STORE_NAME", + "def stream_proxy(original):\n def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)\n\n return LocalProxy(p)" + ], [ "LOAD_NAME", "stream_proxy" @@ -59,6 +171,10 @@ "STORE_ATTR", "sys.stdout" ], + [ + "STORE_NAME", + "def run_server(port, bind_host, show_server_output):\n if not show_server_output:\n thread_proxies[current_thread().ident] = fake_stream()\n try:\n server.app.run(\n debug=True,\n port=port,\n host=bind_host,\n use_reloader=False,\n )\n except socket.error:\n pass" + ], [ "LOAD_NAME", "Environment" @@ -83,6 +199,10 @@ "CALL_FUNCTION_KW", "Environment(\n loader=PackageLoader('birdseye', 'templates'),\n autoescape=select_autoescape(['html', 'xml'])\n)" ], + [ + "STORE_NAME", + "templates_env" + ], [ "LOAD_NAME", "magics_class" @@ -91,10 +211,22 @@ "LOAD_NAME", "Magics" ], + [ + "CALL_FUNCTION", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "CALL_FUNCTION", "magics_class" ], + [ + "STORE_NAME", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], + [ + "STORE_FAST", + " def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)" + ], [ "LOAD_GLOBAL", "LocalProxy" @@ -119,6 +251,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -163,6 +299,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "thread_proxies" @@ -263,6 +403,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )" ], + [ + "STORE_NAME", + "server_url" + ], [ "LOAD_NAME", "Int" @@ -271,6 +415,10 @@ "CALL_FUNCTION_KW", "Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "port" + ], [ "LOAD_NAME", "Unicode" @@ -279,6 +427,10 @@ "CALL_FUNCTION_KW", "Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )" ], + [ + "STORE_NAME", + "bind_host" + ], [ "LOAD_NAME", "Bool" @@ -287,6 +439,10 @@ "CALL_FUNCTION_KW", "Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "show_server_output" + ], [ "LOAD_NAME", "Unicode" @@ -295,6 +451,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )" ], + [ + "STORE_NAME", + "db_url" + ], [ "LOAD_NAME", "cell_magic" @@ -303,6 +463,10 @@ "CALL_FUNCTION", "cell_magic" ], + [ + "STORE_NAME", + " @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "LOAD_DEREF", "self" @@ -463,6 +627,10 @@ "STORE_ATTR", "eye.db" ], + [ + "STORE_FAST", + " def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)" + ], [ "LOAD_GLOBAL", "eye" @@ -483,6 +651,10 @@ "CALL_METHOD", "eye.exec_ipython_cell(cell, callback)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -563,6 +735,10 @@ "CALL_FUNCTION", "HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))" ], + [ + "STORE_FAST", + "html" + ], [ "LOAD_GLOBAL", "display" diff --git a/tests/sample_results/ipython-py-3.9.json b/tests/sample_results/ipython-py-3.9.json index a5714b1..c432edc 100644 --- a/tests/sample_results/ipython-py-3.9.json +++ b/tests/sample_results/ipython-py-3.9.json @@ -1,4 +1,104 @@ [ + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import socket" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from io import BytesIO, StringIO" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from threading import current_thread, Thread" + ], + [ + "STORE_NAME", + "from uuid import uuid4" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.display import HTML, display" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from IPython.core.magic import Magics, cell_magic, magics_class" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from jinja2 import Environment, PackageLoader, select_autoescape" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from traitlets import Unicode, Int, Bool" + ], + [ + "STORE_NAME", + "from werkzeug.local import LocalProxy" + ], + [ + "STORE_NAME", + "from werkzeug.serving import ThreadingMixIn" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye.bird import PY2, Database" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], + [ + "STORE_NAME", + "from birdseye import server, eye" + ], [ "LOAD_NAME", "PY2" @@ -11,6 +111,18 @@ "LOAD_NAME", "StringIO" ], + [ + "STORE_NAME", + "fake_stream" + ], + [ + "STORE_NAME", + "thread_proxies" + ], + [ + "STORE_NAME", + "def stream_proxy(original):\n def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)\n\n return LocalProxy(p)" + ], [ "LOAD_NAME", "stream_proxy" @@ -59,6 +171,10 @@ "STORE_ATTR", "sys.stdout" ], + [ + "STORE_NAME", + "def run_server(port, bind_host, show_server_output):\n if not show_server_output:\n thread_proxies[current_thread().ident] = fake_stream()\n try:\n server.app.run(\n debug=True,\n port=port,\n host=bind_host,\n use_reloader=False,\n )\n except socket.error:\n pass" + ], [ "LOAD_NAME", "Environment" @@ -83,6 +199,10 @@ "CALL_FUNCTION_KW", "Environment(\n loader=PackageLoader('birdseye', 'templates'),\n autoescape=select_autoescape(['html', 'xml'])\n)" ], + [ + "STORE_NAME", + "templates_env" + ], [ "LOAD_NAME", "magics_class" @@ -91,10 +211,22 @@ "LOAD_NAME", "Magics" ], + [ + "CALL_FUNCTION", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "CALL_FUNCTION", "magics_class" ], + [ + "STORE_NAME", + "@magics_class\nclass BirdsEyeMagics(Magics):\n server_url = Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )\n\n port = Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )\n\n bind_host = Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )\n\n show_server_output = Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )\n\n db_url = Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )\n\n @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], + [ + "STORE_FAST", + " def p():\n frame = inspect.currentframe()\n while frame:\n if frame.f_code == ThreadingMixIn.process_request_thread.__code__:\n return fake_stream()\n frame = frame.f_back\n return thread_proxies.get(current_thread().ident,\n original)" + ], [ "LOAD_GLOBAL", "LocalProxy" @@ -119,6 +251,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -163,6 +299,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "thread_proxies" @@ -263,6 +403,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='If set, a server will not be automatically started by %%eye. '\n 'The iframe containing birdseye output will use this value as the base '\n 'of its URL.'\n )" ], + [ + "STORE_NAME", + "server_url" + ], [ "LOAD_NAME", "Int" @@ -271,6 +415,10 @@ "CALL_FUNCTION_KW", "Int(\n 7777, config=True,\n help='Port number for the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "port" + ], [ "LOAD_NAME", "Unicode" @@ -279,6 +427,10 @@ "CALL_FUNCTION_KW", "Unicode(\n '127.0.0.1', config=True,\n help='Host that the server started by %%eye listens on. '\n 'Set to 0.0.0.0 to make it accessible anywhere.'\n )" ], + [ + "STORE_NAME", + "bind_host" + ], [ "LOAD_NAME", "Bool" @@ -287,6 +439,10 @@ "CALL_FUNCTION_KW", "Bool(\n False, config=True,\n help='Set to True to show stdout and stderr from the server started by %%eye.'\n )" ], + [ + "STORE_NAME", + "show_server_output" + ], [ "LOAD_NAME", "Unicode" @@ -295,6 +451,10 @@ "CALL_FUNCTION_KW", "Unicode(\n u'', config=True,\n help='The database URL that the server started by %%eye reads from. '\n 'Equivalent to the environment variable BIRDSEYE_DB.'\n )" ], + [ + "STORE_NAME", + "db_url" + ], [ "LOAD_NAME", "cell_magic" @@ -303,6 +463,10 @@ "CALL_FUNCTION", "cell_magic" ], + [ + "STORE_NAME", + " @cell_magic\n def eye(self, _line, cell):\n if not self.server_url:\n server.db = Database(self.db_url)\n server.Function = server.db.Function\n server.Call = server.db.Call\n server.Session = server.db.Session\n Thread(\n target=run_server,\n args=(\n self.port,\n self.bind_host,\n self.show_server_output,\n ),\n ).start()\n\n eye.db = Database(self.db_url)\n\n def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)\n\n value = eye.exec_ipython_cell(cell, callback)\n # Display the value as would happen if the %eye magic wasn't there\n return value" + ], [ "LOAD_DEREF", "self" @@ -463,6 +627,10 @@ "STORE_ATTR", "eye.db" ], + [ + "STORE_FAST", + " def callback(call_id):\n \"\"\"\n Always executes after the cell, whether or not an exception is raised\n in the user code.\n \"\"\"\n if call_id is None: # probably means a bug\n return\n\n html = HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))\n\n # noinspection PyTypeChecker\n display(html)" + ], [ "LOAD_GLOBAL", "eye" @@ -483,6 +651,10 @@ "CALL_METHOD", "eye.exec_ipython_cell(cell, callback)" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -563,6 +735,10 @@ "CALL_FUNCTION", "HTML(templates_env.get_template('ipython_iframe.html').render(\n call_id=call_id,\n url=self.server_url.rstrip('/'),\n port=self.port,\n container_id=uuid4().hex,\n ))" ], + [ + "STORE_FAST", + "html" + ], [ "LOAD_GLOBAL", "display" diff --git a/tests/sample_results/server-py-3.10.json b/tests/sample_results/server-py-3.10.json index 4dbdad7..d40c3dc 100644 --- a/tests/sample_results/server-py-3.10.json +++ b/tests/sample_results/server-py-3.10.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from os.path import basename" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,70 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import argparse" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask.templating import render_template" + ], + [ + "STORE_NAME", + "from flask_humanize import Humanize" + ], + [ + "STORE_NAME", + "from werkzeug.routing import PathConverter" + ], + [ + "STORE_NAME", + "import sqlalchemy" + ], + [ + "STORE_NAME", + "from birdseye.db import Database" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], [ "LOAD_NAME", "Flask" @@ -19,6 +127,10 @@ "CALL_FUNCTION", "Flask('birdseye')" ], + [ + "STORE_NAME", + "app" + ], [ "LOAD_NAME", "app" @@ -47,6 +159,14 @@ "LOAD_NAME", "PathConverter" ], + [ + "CALL_FUNCTION", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], [ "LOAD_NAME", "FileConverter" @@ -75,6 +195,10 @@ "CALL_FUNCTION", "Database()" ], + [ + "STORE_NAME", + "db" + ], [ "LOAD_NAME", "db" @@ -83,6 +207,10 @@ "LOAD_ATTR", "db.Session" ], + [ + "STORE_NAME", + "Session" + ], [ "LOAD_NAME", "db" @@ -91,6 +219,10 @@ "LOAD_ATTR", "db.Function" ], + [ + "STORE_NAME", + "Function" + ], [ "LOAD_NAME", "db" @@ -99,6 +231,10 @@ "LOAD_ATTR", "db.Call" ], + [ + "STORE_NAME", + "Call" + ], [ "LOAD_NAME", "app" @@ -127,6 +263,10 @@ "CALL_FUNCTION", "app.route('/')" ], + [ + "STORE_NAME", + "@app.route('/')\n@db.provide_session\ndef index(session):\n all_paths = db.all_file_paths()\n\n recent_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100])\n\n files = OrderedDict()\n\n for row in recent_calls:\n if is_ipython_cell(row.file):\n continue\n files.setdefault(\n row.file, OrderedDict()\n ).setdefault(\n row.name, row\n )\n\n for path in all_paths:\n files.setdefault(\n path, OrderedDict()\n )\n\n short = partial(short_path, all_paths=all_paths)\n\n return render_template('index.html',\n short=short,\n files=files)" + ], [ "LOAD_NAME", "app" @@ -155,6 +295,10 @@ "CALL_FUNCTION", "app.route('/file/')" ], + [ + "STORE_NAME", + "@app.route('/file/')\n@db.provide_session\ndef file_view(session, path):\n path = fix_abs_path(path)\n\n # Get all calls and functions in this file\n filtered_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls'))\n\n # Get the latest call *time* for each function in the file\n latest_calls = session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')\n\n # Get the latest call for each function\n query = session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())\n funcs = group_by_attr(query, 'type')\n\n # Add any functions which were never called\n all_funcs = sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())\n func_names = {row.name for row in query}\n for func in all_funcs:\n if func.name not in func_names:\n funcs[func.type].append(func)\n\n return render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" + ], [ "LOAD_NAME", "app" @@ -183,6 +327,10 @@ "CALL_FUNCTION", "app.route('/file//__function__/')" ], + [ + "STORE_NAME", + "@app.route('/file//__function__/')\n@db.provide_session\ndef func_view(session, path, func_name):\n path = fix_abs_path(path)\n query = get_calls(session, path, func_name, 200)\n if query:\n func = query[0]\n calls = [withattrs(Call(), **row._asdict()) for row in query]\n else:\n func = session.query(Function).filter_by(file=path, name=func_name)[0]\n calls = None\n\n return render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" + ], [ "LOAD_NAME", "app" @@ -211,6 +359,14 @@ "CALL_FUNCTION", "app.route('/api/file//__function__//latest_call/')" ], + [ + "STORE_NAME", + "@app.route('/api/file//__function__//latest_call/')\n@db.provide_session\ndef latest_call(session, path, func_name):\n path = fix_abs_path(path)\n call = get_calls(session, path, func_name, 1)[0]\n return jsonify(dict(\n id=call.id,\n url=url_for(call_view.__name__,\n call_id=call.id),\n ))" + ], + [ + "STORE_NAME", + "def get_calls(session, path, func_name, limit):\n return (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path, name=func_name)\n .order_by(Call.start_time.desc())[:limit])" + ], [ "LOAD_NAME", "db" @@ -223,6 +379,10 @@ "CALL_FUNCTION", "db.provide_session" ], + [ + "STORE_NAME", + "@db.provide_session\ndef base_call_view(session, call_id, template):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return render_template(template,\n short_path=basename(func.file),\n call=call,\n func=func)" + ], [ "LOAD_NAME", "app" @@ -239,6 +399,10 @@ "CALL_FUNCTION", "app.route('/call/')" ], + [ + "STORE_NAME", + "@app.route('/call/')\ndef call_view(call_id):\n return base_call_view(call_id, 'call.html')" + ], [ "LOAD_NAME", "app" @@ -255,6 +419,10 @@ "CALL_FUNCTION", "app.route('/ipython_call/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_call/')\ndef ipython_call_view(call_id):\n return base_call_view(call_id, 'ipython_call.html')" + ], [ "LOAD_NAME", "app" @@ -271,6 +439,10 @@ "CALL_FUNCTION", "app.route('/ipython_iframe/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_iframe/')\ndef ipython_iframe_view(call_id):\n \"\"\"\n This view isn't generally used, it's just an easy way to play with the template\n without a notebook.\n \"\"\"\n return render_template('ipython_iframe.html',\n container_id='1234',\n port=7777,\n call_id=call_id)" + ], [ "LOAD_NAME", "app" @@ -287,6 +459,10 @@ "CALL_FUNCTION", "app.route('/kill', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/kill', methods=['POST'])\ndef kill():\n func = request.environ.get('werkzeug.server.shutdown')\n if func is None:\n raise RuntimeError('Not running with the Werkzeug Server')\n func()\n return 'Server shutting down...'" + ], [ "LOAD_NAME", "app" @@ -315,6 +491,10 @@ "CALL_FUNCTION", "app.route('/api/call/')" ], + [ + "STORE_NAME", + "@app.route('/api/call/')\n@db.provide_session\ndef api_call_view(session, call_id):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return DecentJSONEncoder().encode(dict(\n call=dict(data=call.parsed_data, **Call.basic_dict(call)),\n function=dict(data=func.parsed_data, **Function.basic_dict(func))))" + ], [ "LOAD_NAME", "app" @@ -343,6 +523,10 @@ "CALL_FUNCTION", "app.route('/api/calls_by_body_hash/')" ], + [ + "STORE_NAME", + "@app.route('/api/calls_by_body_hash/')\n@db.provide_session\ndef calls_by_body_hash(session, body_hash):\n query = (session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200])\n\n calls = [Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]\n\n function_data_set = {row.data for row in query}\n ranges = set()\n loop_ranges = set()\n for function_data in function_data_set:\n function_data = json.loads(function_data)\n\n def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))\n\n add('node_ranges', ranges)\n\n # All functions are expected to have the same set\n # of loop nodes\n current_loop_ranges = set()\n add('loop_ranges', current_loop_ranges)\n assert loop_ranges in (set(), current_loop_ranges)\n loop_ranges = current_loop_ranges\n\n ranges = [dict(start=start, end=end) for start, end in ranges]\n loop_ranges = [dict(start=start, end=end) for start, end in loop_ranges]\n\n return DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" + ], [ "LOAD_NAME", "app" @@ -371,6 +555,10 @@ "CALL_FUNCTION", "app.route('/api/body_hashes_present/', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/api/body_hashes_present/', methods=['POST'])\n@db.provide_session\ndef body_hashes_present(session):\n hashes = request.get_json()\n query = (session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash))\n return DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" + ], [ "LOAD_NAME", "sys" @@ -383,6 +571,10 @@ "BINARY_SUBSCR", "sys.argv[1:]" ], + [ + "STORE_NAME", + "def main(argv=sys.argv[1:]):\n # Support legacy CLI where there was just one positional argument: the port\n if len(argv) == 1 and argv[0].isdigit():\n argv.insert(0, '--port')\n\n parser = argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")\n parser.add_argument('-p', '--port', help='HTTP port, default is 7777', default=7777, type=int)\n parser.add_argument('--host', help=\"HTTP host, default is 'localhost'\", default='localhost')\n\n args = parser.parse_args(argv)\n app.run(\n port=args.port,\n host=args.host,\n use_reloader=os.environ.get('BIRDSEYE_RELOADER') == '1',\n )" + ], [ "LOAD_NAME", "__name__" @@ -399,6 +591,22 @@ "CALL_FUNCTION", "main()" ], + [ + "LOAD_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "regex" + ], [ "LOAD_GLOBAL", "db" @@ -411,6 +619,10 @@ "CALL_METHOD", "db.all_file_paths()" ], + [ + "STORE_FAST", + "all_paths" + ], [ "LOAD_FAST", "session" @@ -483,6 +695,10 @@ "BINARY_SUBSCR", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100]" ], + [ + "STORE_FAST", + "recent_calls" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -491,10 +707,18 @@ "CALL_FUNCTION", "OrderedDict()" ], + [ + "STORE_FAST", + "files" + ], [ "LOAD_FAST", "recent_calls" ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -563,6 +787,10 @@ "LOAD_FAST", "all_paths" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "files" @@ -603,6 +831,10 @@ "CALL_FUNCTION_KW", "partial(short_path, all_paths=all_paths)" ], + [ + "STORE_FAST", + "short" + ], [ "LOAD_GLOBAL", "render_template" @@ -631,6 +863,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "session" @@ -695,6 +931,10 @@ "CALL_METHOD", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls')" ], + [ + "STORE_FAST", + "filtered_calls" + ], [ "LOAD_FAST", "session" @@ -783,6 +1023,10 @@ "CALL_METHOD", "session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')" ], + [ + "STORE_FAST", + "latest_calls" + ], [ "LOAD_FAST", "session" @@ -907,6 +1151,10 @@ "CALL_METHOD", "session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "group_by_attr" @@ -919,6 +1167,10 @@ "CALL_FUNCTION", "group_by_attr(query, 'type')" ], + [ + "STORE_FAST", + "funcs" + ], [ "LOAD_GLOBAL", "sorted" @@ -975,14 +1227,30 @@ "CALL_FUNCTION", "sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())" ], + [ + "STORE_FAST", + "all_funcs" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "func_names" + ], [ "LOAD_FAST", "all_funcs" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1067,6 +1335,14 @@ "CALL_FUNCTION_KW", "render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" ], + [ + "LOAD_FAST", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1087,6 +1363,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1107,6 +1387,10 @@ "CALL_FUNCTION", "get_calls(session, path, func_name, 200)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" @@ -1119,10 +1403,22 @@ "BINARY_SUBSCR", "query[0]" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "session" @@ -1159,6 +1455,14 @@ "BINARY_SUBSCR", "session.query(Function).filter_by(file=path, name=func_name)[0]" ], + [ + "STORE_FAST", + "func" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_GLOBAL", "render_template" @@ -1187,6 +1491,14 @@ "CALL_FUNCTION_KW", "render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" ], + [ + "LOAD_FAST", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "withattrs" @@ -1227,6 +1539,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1251,6 +1567,10 @@ "BINARY_SUBSCR", "get_calls(session, path, func_name, 1)[0]" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_GLOBAL", "jsonify" @@ -1427,6 +1747,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1435,6 +1759,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "render_template" @@ -1523,6 +1851,10 @@ "CALL_METHOD", "request.environ.get('werkzeug.server.shutdown')" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1583,6 +1915,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1591,6 +1927,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1763,14 +2103,34 @@ "BINARY_SUBSCR", "session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200]" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "function_data_set" + ], [ "LOAD_GLOBAL", "set" @@ -1779,6 +2139,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_GLOBAL", "set" @@ -1787,10 +2151,18 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "function_data_set" ], + [ + "STORE_DEREF", + "function_data" + ], [ "LOAD_GLOBAL", "json" @@ -1807,6 +2179,14 @@ "CALL_METHOD", "json.loads(function_data)" ], + [ + "STORE_DEREF", + "function_data" + ], + [ + "STORE_FAST", + " def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))" + ], [ "LOAD_FAST", "add" @@ -1827,6 +2207,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "current_loop_ranges" + ], [ "LOAD_FAST", "add" @@ -1863,14 +2247,34 @@ "LOAD_FAST", "current_loop_ranges" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_FAST", "loop_ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1907,6 +2311,14 @@ "CALL_METHOD", "DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" ], + [ + "LOAD_FAST", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "Call" @@ -1947,6 +2359,14 @@ "CALL_METHOD", "Call.basic_dict(withattrs(Call(), **row._asdict()))" ], + [ + "LOAD_FAST", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1967,6 +2387,10 @@ "BINARY_SUBSCR", "function_data[key]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "ranges_set" @@ -1995,6 +2419,18 @@ "CALL_METHOD", "ranges_set.add((node['start'], node['end']))" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2011,6 +2447,18 @@ "CALL_FUNCTION_KW", "dict(start=start, end=end)" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2039,6 +2487,10 @@ "CALL_METHOD", "request.get_json()" ], + [ + "STORE_FAST", + "hashes" + ], [ "LOAD_FAST", "session" @@ -2139,6 +2591,10 @@ "CALL_METHOD", "session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -2155,10 +2611,26 @@ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], [ "CALL_METHOD", "DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" ], + [ + "LOAD_FAST", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "count" + ], [ "LOAD_GLOBAL", "dict" @@ -2231,6 +2703,10 @@ "CALL_FUNCTION_KW", "argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")" ], + [ + "STORE_FAST", + "parser" + ], [ "LOAD_FAST", "parser" @@ -2275,6 +2751,10 @@ "CALL_METHOD", "parser.parse_args(argv)" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "app" diff --git a/tests/sample_results/server-py-3.8.json b/tests/sample_results/server-py-3.8.json index b51469d..a63058c 100644 --- a/tests/sample_results/server-py-3.8.json +++ b/tests/sample_results/server-py-3.8.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from os.path import basename" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,70 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import argparse" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask.templating import render_template" + ], + [ + "STORE_NAME", + "from flask_humanize import Humanize" + ], + [ + "STORE_NAME", + "from werkzeug.routing import PathConverter" + ], + [ + "STORE_NAME", + "import sqlalchemy" + ], + [ + "STORE_NAME", + "from birdseye.db import Database" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], [ "LOAD_NAME", "Flask" @@ -19,6 +127,10 @@ "CALL_FUNCTION", "Flask('birdseye')" ], + [ + "STORE_NAME", + "app" + ], [ "LOAD_NAME", "app" @@ -47,6 +159,14 @@ "LOAD_NAME", "PathConverter" ], + [ + "CALL_FUNCTION", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], [ "LOAD_NAME", "FileConverter" @@ -75,6 +195,10 @@ "CALL_FUNCTION", "Database()" ], + [ + "STORE_NAME", + "db" + ], [ "LOAD_NAME", "db" @@ -83,6 +207,10 @@ "LOAD_ATTR", "db.Session" ], + [ + "STORE_NAME", + "Session" + ], [ "LOAD_NAME", "db" @@ -91,6 +219,10 @@ "LOAD_ATTR", "db.Function" ], + [ + "STORE_NAME", + "Function" + ], [ "LOAD_NAME", "db" @@ -99,6 +231,10 @@ "LOAD_ATTR", "db.Call" ], + [ + "STORE_NAME", + "Call" + ], [ "LOAD_NAME", "app" @@ -127,6 +263,10 @@ "CALL_FUNCTION", "app.route('/')" ], + [ + "STORE_NAME", + "@app.route('/')\n@db.provide_session\ndef index(session):\n all_paths = db.all_file_paths()\n\n recent_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100])\n\n files = OrderedDict()\n\n for row in recent_calls:\n if is_ipython_cell(row.file):\n continue\n files.setdefault(\n row.file, OrderedDict()\n ).setdefault(\n row.name, row\n )\n\n for path in all_paths:\n files.setdefault(\n path, OrderedDict()\n )\n\n short = partial(short_path, all_paths=all_paths)\n\n return render_template('index.html',\n short=short,\n files=files)" + ], [ "LOAD_NAME", "app" @@ -155,6 +295,10 @@ "CALL_FUNCTION", "app.route('/file/')" ], + [ + "STORE_NAME", + "@app.route('/file/')\n@db.provide_session\ndef file_view(session, path):\n path = fix_abs_path(path)\n\n # Get all calls and functions in this file\n filtered_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls'))\n\n # Get the latest call *time* for each function in the file\n latest_calls = session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')\n\n # Get the latest call for each function\n query = session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())\n funcs = group_by_attr(query, 'type')\n\n # Add any functions which were never called\n all_funcs = sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())\n func_names = {row.name for row in query}\n for func in all_funcs:\n if func.name not in func_names:\n funcs[func.type].append(func)\n\n return render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" + ], [ "LOAD_NAME", "app" @@ -183,6 +327,10 @@ "CALL_FUNCTION", "app.route('/file//__function__/')" ], + [ + "STORE_NAME", + "@app.route('/file//__function__/')\n@db.provide_session\ndef func_view(session, path, func_name):\n path = fix_abs_path(path)\n query = get_calls(session, path, func_name, 200)\n if query:\n func = query[0]\n calls = [withattrs(Call(), **row._asdict()) for row in query]\n else:\n func = session.query(Function).filter_by(file=path, name=func_name)[0]\n calls = None\n\n return render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" + ], [ "LOAD_NAME", "app" @@ -211,6 +359,14 @@ "CALL_FUNCTION", "app.route('/api/file//__function__//latest_call/')" ], + [ + "STORE_NAME", + "@app.route('/api/file//__function__//latest_call/')\n@db.provide_session\ndef latest_call(session, path, func_name):\n path = fix_abs_path(path)\n call = get_calls(session, path, func_name, 1)[0]\n return jsonify(dict(\n id=call.id,\n url=url_for(call_view.__name__,\n call_id=call.id),\n ))" + ], + [ + "STORE_NAME", + "def get_calls(session, path, func_name, limit):\n return (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path, name=func_name)\n .order_by(Call.start_time.desc())[:limit])" + ], [ "LOAD_NAME", "db" @@ -223,6 +379,10 @@ "CALL_FUNCTION", "db.provide_session" ], + [ + "STORE_NAME", + "@db.provide_session\ndef base_call_view(session, call_id, template):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return render_template(template,\n short_path=basename(func.file),\n call=call,\n func=func)" + ], [ "LOAD_NAME", "app" @@ -239,6 +399,10 @@ "CALL_FUNCTION", "app.route('/call/')" ], + [ + "STORE_NAME", + "@app.route('/call/')\ndef call_view(call_id):\n return base_call_view(call_id, 'call.html')" + ], [ "LOAD_NAME", "app" @@ -255,6 +419,10 @@ "CALL_FUNCTION", "app.route('/ipython_call/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_call/')\ndef ipython_call_view(call_id):\n return base_call_view(call_id, 'ipython_call.html')" + ], [ "LOAD_NAME", "app" @@ -271,6 +439,10 @@ "CALL_FUNCTION", "app.route('/ipython_iframe/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_iframe/')\ndef ipython_iframe_view(call_id):\n \"\"\"\n This view isn't generally used, it's just an easy way to play with the template\n without a notebook.\n \"\"\"\n return render_template('ipython_iframe.html',\n container_id='1234',\n port=7777,\n call_id=call_id)" + ], [ "LOAD_NAME", "app" @@ -287,6 +459,10 @@ "CALL_FUNCTION", "app.route('/kill', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/kill', methods=['POST'])\ndef kill():\n func = request.environ.get('werkzeug.server.shutdown')\n if func is None:\n raise RuntimeError('Not running with the Werkzeug Server')\n func()\n return 'Server shutting down...'" + ], [ "LOAD_NAME", "app" @@ -315,6 +491,10 @@ "CALL_FUNCTION", "app.route('/api/call/')" ], + [ + "STORE_NAME", + "@app.route('/api/call/')\n@db.provide_session\ndef api_call_view(session, call_id):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return DecentJSONEncoder().encode(dict(\n call=dict(data=call.parsed_data, **Call.basic_dict(call)),\n function=dict(data=func.parsed_data, **Function.basic_dict(func))))" + ], [ "LOAD_NAME", "app" @@ -343,6 +523,10 @@ "CALL_FUNCTION", "app.route('/api/calls_by_body_hash/')" ], + [ + "STORE_NAME", + "@app.route('/api/calls_by_body_hash/')\n@db.provide_session\ndef calls_by_body_hash(session, body_hash):\n query = (session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200])\n\n calls = [Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]\n\n function_data_set = {row.data for row in query}\n ranges = set()\n loop_ranges = set()\n for function_data in function_data_set:\n function_data = json.loads(function_data)\n\n def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))\n\n add('node_ranges', ranges)\n\n # All functions are expected to have the same set\n # of loop nodes\n current_loop_ranges = set()\n add('loop_ranges', current_loop_ranges)\n assert loop_ranges in (set(), current_loop_ranges)\n loop_ranges = current_loop_ranges\n\n ranges = [dict(start=start, end=end) for start, end in ranges]\n loop_ranges = [dict(start=start, end=end) for start, end in loop_ranges]\n\n return DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" + ], [ "LOAD_NAME", "app" @@ -371,6 +555,10 @@ "CALL_FUNCTION", "app.route('/api/body_hashes_present/', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/api/body_hashes_present/', methods=['POST'])\n@db.provide_session\ndef body_hashes_present(session):\n hashes = request.get_json()\n query = (session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash))\n return DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" + ], [ "LOAD_NAME", "sys" @@ -383,6 +571,10 @@ "BINARY_SUBSCR", "sys.argv[1:]" ], + [ + "STORE_NAME", + "def main(argv=sys.argv[1:]):\n # Support legacy CLI where there was just one positional argument: the port\n if len(argv) == 1 and argv[0].isdigit():\n argv.insert(0, '--port')\n\n parser = argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")\n parser.add_argument('-p', '--port', help='HTTP port, default is 7777', default=7777, type=int)\n parser.add_argument('--host', help=\"HTTP host, default is 'localhost'\", default='localhost')\n\n args = parser.parse_args(argv)\n app.run(\n port=args.port,\n host=args.host,\n use_reloader=os.environ.get('BIRDSEYE_RELOADER') == '1',\n )" + ], [ "LOAD_NAME", "__name__" @@ -399,6 +591,22 @@ "CALL_FUNCTION", "main()" ], + [ + "LOAD_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "regex" + ], [ "LOAD_GLOBAL", "db" @@ -411,6 +619,10 @@ "CALL_METHOD", "db.all_file_paths()" ], + [ + "STORE_FAST", + "all_paths" + ], [ "LOAD_FAST", "session" @@ -483,6 +695,10 @@ "BINARY_SUBSCR", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100]" ], + [ + "STORE_FAST", + "recent_calls" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -491,10 +707,18 @@ "CALL_FUNCTION", "OrderedDict()" ], + [ + "STORE_FAST", + "files" + ], [ "LOAD_FAST", "recent_calls" ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -563,6 +787,10 @@ "LOAD_FAST", "all_paths" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "files" @@ -603,6 +831,10 @@ "CALL_FUNCTION_KW", "partial(short_path, all_paths=all_paths)" ], + [ + "STORE_FAST", + "short" + ], [ "LOAD_GLOBAL", "render_template" @@ -631,6 +863,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "session" @@ -695,6 +931,10 @@ "CALL_METHOD", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls')" ], + [ + "STORE_FAST", + "filtered_calls" + ], [ "LOAD_FAST", "session" @@ -783,6 +1023,10 @@ "CALL_METHOD", "session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')" ], + [ + "STORE_FAST", + "latest_calls" + ], [ "LOAD_FAST", "session" @@ -907,6 +1151,10 @@ "CALL_METHOD", "session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "group_by_attr" @@ -919,6 +1167,10 @@ "CALL_FUNCTION", "group_by_attr(query, 'type')" ], + [ + "STORE_FAST", + "funcs" + ], [ "LOAD_GLOBAL", "sorted" @@ -975,14 +1227,30 @@ "CALL_FUNCTION", "sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())" ], + [ + "STORE_FAST", + "all_funcs" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "func_names" + ], [ "LOAD_FAST", "all_funcs" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1067,6 +1335,14 @@ "CALL_FUNCTION_KW", "render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" ], + [ + "LOAD_FAST", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1087,6 +1363,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1107,6 +1387,10 @@ "CALL_FUNCTION", "get_calls(session, path, func_name, 200)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" @@ -1119,10 +1403,22 @@ "BINARY_SUBSCR", "query[0]" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "session" @@ -1159,6 +1455,14 @@ "BINARY_SUBSCR", "session.query(Function).filter_by(file=path, name=func_name)[0]" ], + [ + "STORE_FAST", + "func" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_GLOBAL", "render_template" @@ -1187,6 +1491,14 @@ "CALL_FUNCTION_KW", "render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" ], + [ + "LOAD_FAST", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "withattrs" @@ -1227,6 +1539,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1251,6 +1567,10 @@ "BINARY_SUBSCR", "get_calls(session, path, func_name, 1)[0]" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_GLOBAL", "jsonify" @@ -1427,6 +1747,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1435,6 +1759,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "render_template" @@ -1523,6 +1851,10 @@ "CALL_METHOD", "request.environ.get('werkzeug.server.shutdown')" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1583,6 +1915,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1591,6 +1927,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1763,14 +2103,34 @@ "BINARY_SUBSCR", "session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200]" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "function_data_set" + ], [ "LOAD_GLOBAL", "set" @@ -1779,6 +2139,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_GLOBAL", "set" @@ -1787,10 +2151,18 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "function_data_set" ], + [ + "STORE_DEREF", + "function_data" + ], [ "LOAD_GLOBAL", "json" @@ -1807,6 +2179,14 @@ "CALL_METHOD", "json.loads(function_data)" ], + [ + "STORE_DEREF", + "function_data" + ], + [ + "STORE_FAST", + " def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))" + ], [ "LOAD_FAST", "add" @@ -1827,6 +2207,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "current_loop_ranges" + ], [ "LOAD_FAST", "add" @@ -1863,14 +2247,34 @@ "LOAD_FAST", "current_loop_ranges" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_FAST", "loop_ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1907,6 +2311,14 @@ "CALL_METHOD", "DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" ], + [ + "LOAD_FAST", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "Call" @@ -1947,6 +2359,14 @@ "CALL_METHOD", "Call.basic_dict(withattrs(Call(), **row._asdict()))" ], + [ + "LOAD_FAST", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1967,6 +2387,10 @@ "BINARY_SUBSCR", "function_data[key]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "ranges_set" @@ -1995,6 +2419,18 @@ "CALL_METHOD", "ranges_set.add((node['start'], node['end']))" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2011,6 +2447,18 @@ "CALL_FUNCTION_KW", "dict(start=start, end=end)" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2039,6 +2487,10 @@ "CALL_METHOD", "request.get_json()" ], + [ + "STORE_FAST", + "hashes" + ], [ "LOAD_FAST", "session" @@ -2139,6 +2591,10 @@ "CALL_METHOD", "session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -2155,10 +2611,26 @@ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], [ "CALL_METHOD", "DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" ], + [ + "LOAD_FAST", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "count" + ], [ "LOAD_GLOBAL", "dict" @@ -2231,6 +2703,10 @@ "CALL_FUNCTION_KW", "argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")" ], + [ + "STORE_FAST", + "parser" + ], [ "LOAD_FAST", "parser" @@ -2275,6 +2751,10 @@ "CALL_METHOD", "parser.parse_args(argv)" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "app" diff --git a/tests/sample_results/server-py-3.9.json b/tests/sample_results/server-py-3.9.json index 4dbdad7..d40c3dc 100644 --- a/tests/sample_results/server-py-3.9.json +++ b/tests/sample_results/server-py-3.9.json @@ -1,4 +1,48 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "from functools import partial" + ], + [ + "STORE_NAME", + "from os.path import basename" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], + [ + "STORE_NAME", + "from littleutils import DecentJSONEncoder, withattrs, group_by_attr" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +55,70 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import argparse" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask import Flask, request, jsonify, url_for" + ], + [ + "STORE_NAME", + "from flask.templating import render_template" + ], + [ + "STORE_NAME", + "from flask_humanize import Humanize" + ], + [ + "STORE_NAME", + "from werkzeug.routing import PathConverter" + ], + [ + "STORE_NAME", + "import sqlalchemy" + ], + [ + "STORE_NAME", + "from birdseye.db import Database" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], + [ + "STORE_NAME", + "from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell" + ], [ "LOAD_NAME", "Flask" @@ -19,6 +127,10 @@ "CALL_FUNCTION", "Flask('birdseye')" ], + [ + "STORE_NAME", + "app" + ], [ "LOAD_NAME", "app" @@ -47,6 +159,14 @@ "LOAD_NAME", "PathConverter" ], + [ + "CALL_FUNCTION", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], [ "LOAD_NAME", "FileConverter" @@ -75,6 +195,10 @@ "CALL_FUNCTION", "Database()" ], + [ + "STORE_NAME", + "db" + ], [ "LOAD_NAME", "db" @@ -83,6 +207,10 @@ "LOAD_ATTR", "db.Session" ], + [ + "STORE_NAME", + "Session" + ], [ "LOAD_NAME", "db" @@ -91,6 +219,10 @@ "LOAD_ATTR", "db.Function" ], + [ + "STORE_NAME", + "Function" + ], [ "LOAD_NAME", "db" @@ -99,6 +231,10 @@ "LOAD_ATTR", "db.Call" ], + [ + "STORE_NAME", + "Call" + ], [ "LOAD_NAME", "app" @@ -127,6 +263,10 @@ "CALL_FUNCTION", "app.route('/')" ], + [ + "STORE_NAME", + "@app.route('/')\n@db.provide_session\ndef index(session):\n all_paths = db.all_file_paths()\n\n recent_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100])\n\n files = OrderedDict()\n\n for row in recent_calls:\n if is_ipython_cell(row.file):\n continue\n files.setdefault(\n row.file, OrderedDict()\n ).setdefault(\n row.name, row\n )\n\n for path in all_paths:\n files.setdefault(\n path, OrderedDict()\n )\n\n short = partial(short_path, all_paths=all_paths)\n\n return render_template('index.html',\n short=short,\n files=files)" + ], [ "LOAD_NAME", "app" @@ -155,6 +295,10 @@ "CALL_FUNCTION", "app.route('/file/')" ], + [ + "STORE_NAME", + "@app.route('/file/')\n@db.provide_session\ndef file_view(session, path):\n path = fix_abs_path(path)\n\n # Get all calls and functions in this file\n filtered_calls = (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls'))\n\n # Get the latest call *time* for each function in the file\n latest_calls = session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')\n\n # Get the latest call for each function\n query = session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())\n funcs = group_by_attr(query, 'type')\n\n # Add any functions which were never called\n all_funcs = sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())\n func_names = {row.name for row in query}\n for func in all_funcs:\n if func.name not in func_names:\n funcs[func.type].append(func)\n\n return render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" + ], [ "LOAD_NAME", "app" @@ -183,6 +327,10 @@ "CALL_FUNCTION", "app.route('/file//__function__/')" ], + [ + "STORE_NAME", + "@app.route('/file//__function__/')\n@db.provide_session\ndef func_view(session, path, func_name):\n path = fix_abs_path(path)\n query = get_calls(session, path, func_name, 200)\n if query:\n func = query[0]\n calls = [withattrs(Call(), **row._asdict()) for row in query]\n else:\n func = session.query(Function).filter_by(file=path, name=func_name)[0]\n calls = None\n\n return render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" + ], [ "LOAD_NAME", "app" @@ -211,6 +359,14 @@ "CALL_FUNCTION", "app.route('/api/file//__function__//latest_call/')" ], + [ + "STORE_NAME", + "@app.route('/api/file//__function__//latest_call/')\n@db.provide_session\ndef latest_call(session, path, func_name):\n path = fix_abs_path(path)\n call = get_calls(session, path, func_name, 1)[0]\n return jsonify(dict(\n id=call.id,\n url=url_for(call_view.__name__,\n call_id=call.id),\n ))" + ], + [ + "STORE_NAME", + "def get_calls(session, path, func_name, limit):\n return (session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path, name=func_name)\n .order_by(Call.start_time.desc())[:limit])" + ], [ "LOAD_NAME", "db" @@ -223,6 +379,10 @@ "CALL_FUNCTION", "db.provide_session" ], + [ + "STORE_NAME", + "@db.provide_session\ndef base_call_view(session, call_id, template):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return render_template(template,\n short_path=basename(func.file),\n call=call,\n func=func)" + ], [ "LOAD_NAME", "app" @@ -239,6 +399,10 @@ "CALL_FUNCTION", "app.route('/call/')" ], + [ + "STORE_NAME", + "@app.route('/call/')\ndef call_view(call_id):\n return base_call_view(call_id, 'call.html')" + ], [ "LOAD_NAME", "app" @@ -255,6 +419,10 @@ "CALL_FUNCTION", "app.route('/ipython_call/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_call/')\ndef ipython_call_view(call_id):\n return base_call_view(call_id, 'ipython_call.html')" + ], [ "LOAD_NAME", "app" @@ -271,6 +439,10 @@ "CALL_FUNCTION", "app.route('/ipython_iframe/')" ], + [ + "STORE_NAME", + "@app.route('/ipython_iframe/')\ndef ipython_iframe_view(call_id):\n \"\"\"\n This view isn't generally used, it's just an easy way to play with the template\n without a notebook.\n \"\"\"\n return render_template('ipython_iframe.html',\n container_id='1234',\n port=7777,\n call_id=call_id)" + ], [ "LOAD_NAME", "app" @@ -287,6 +459,10 @@ "CALL_FUNCTION", "app.route('/kill', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/kill', methods=['POST'])\ndef kill():\n func = request.environ.get('werkzeug.server.shutdown')\n if func is None:\n raise RuntimeError('Not running with the Werkzeug Server')\n func()\n return 'Server shutting down...'" + ], [ "LOAD_NAME", "app" @@ -315,6 +491,10 @@ "CALL_FUNCTION", "app.route('/api/call/')" ], + [ + "STORE_NAME", + "@app.route('/api/call/')\n@db.provide_session\ndef api_call_view(session, call_id):\n call = session.query(Call).filter_by(id=call_id).one()\n func = call.function\n return DecentJSONEncoder().encode(dict(\n call=dict(data=call.parsed_data, **Call.basic_dict(call)),\n function=dict(data=func.parsed_data, **Function.basic_dict(func))))" + ], [ "LOAD_NAME", "app" @@ -343,6 +523,10 @@ "CALL_FUNCTION", "app.route('/api/calls_by_body_hash/')" ], + [ + "STORE_NAME", + "@app.route('/api/calls_by_body_hash/')\n@db.provide_session\ndef calls_by_body_hash(session, body_hash):\n query = (session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200])\n\n calls = [Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]\n\n function_data_set = {row.data for row in query}\n ranges = set()\n loop_ranges = set()\n for function_data in function_data_set:\n function_data = json.loads(function_data)\n\n def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))\n\n add('node_ranges', ranges)\n\n # All functions are expected to have the same set\n # of loop nodes\n current_loop_ranges = set()\n add('loop_ranges', current_loop_ranges)\n assert loop_ranges in (set(), current_loop_ranges)\n loop_ranges = current_loop_ranges\n\n ranges = [dict(start=start, end=end) for start, end in ranges]\n loop_ranges = [dict(start=start, end=end) for start, end in loop_ranges]\n\n return DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" + ], [ "LOAD_NAME", "app" @@ -371,6 +555,10 @@ "CALL_FUNCTION", "app.route('/api/body_hashes_present/', methods=['POST'])" ], + [ + "STORE_NAME", + "@app.route('/api/body_hashes_present/', methods=['POST'])\n@db.provide_session\ndef body_hashes_present(session):\n hashes = request.get_json()\n query = (session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash))\n return DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" + ], [ "LOAD_NAME", "sys" @@ -383,6 +571,10 @@ "BINARY_SUBSCR", "sys.argv[1:]" ], + [ + "STORE_NAME", + "def main(argv=sys.argv[1:]):\n # Support legacy CLI where there was just one positional argument: the port\n if len(argv) == 1 and argv[0].isdigit():\n argv.insert(0, '--port')\n\n parser = argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")\n parser.add_argument('-p', '--port', help='HTTP port, default is 7777', default=7777, type=int)\n parser.add_argument('--host', help=\"HTTP host, default is 'localhost'\", default='localhost')\n\n args = parser.parse_args(argv)\n app.run(\n port=args.port,\n host=args.host,\n use_reloader=os.environ.get('BIRDSEYE_RELOADER') == '1',\n )" + ], [ "LOAD_NAME", "__name__" @@ -399,6 +591,22 @@ "CALL_FUNCTION", "main()" ], + [ + "LOAD_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "class FileConverter(PathConverter):\n regex = '.*?'" + ], + [ + "STORE_NAME", + "regex" + ], [ "LOAD_GLOBAL", "db" @@ -411,6 +619,10 @@ "CALL_METHOD", "db.all_file_paths()" ], + [ + "STORE_FAST", + "all_paths" + ], [ "LOAD_FAST", "session" @@ -483,6 +695,10 @@ "BINARY_SUBSCR", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .order_by(Call.start_time.desc())[:100]" ], + [ + "STORE_FAST", + "recent_calls" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -491,10 +707,18 @@ "CALL_FUNCTION", "OrderedDict()" ], + [ + "STORE_FAST", + "files" + ], [ "LOAD_FAST", "recent_calls" ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -563,6 +787,10 @@ "LOAD_FAST", "all_paths" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "files" @@ -603,6 +831,10 @@ "CALL_FUNCTION_KW", "partial(short_path, all_paths=all_paths)" ], + [ + "STORE_FAST", + "short" + ], [ "LOAD_GLOBAL", "render_template" @@ -631,6 +863,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_FAST", "session" @@ -695,6 +931,10 @@ "CALL_METHOD", "session.query(*(Call.basic_columns + Function.basic_columns))\n .join(Function)\n .filter_by(file=path)\n .subquery('filtered_calls')" ], + [ + "STORE_FAST", + "filtered_calls" + ], [ "LOAD_FAST", "session" @@ -783,6 +1023,10 @@ "CALL_METHOD", "session.query(\n filtered_calls.c.name,\n sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime')\n ).group_by(\n filtered_calls.c.name,\n ).subquery('latest_calls')" ], + [ + "STORE_FAST", + "latest_calls" + ], [ "LOAD_FAST", "session" @@ -907,6 +1151,10 @@ "CALL_METHOD", "session.query(filtered_calls).join(\n latest_calls,\n sqlalchemy.and_(\n filtered_calls.c.name == latest_calls.c.name,\n filtered_calls.c.start_time == latest_calls.c.maxtime,\n )\n ).order_by(filtered_calls.c.start_time.desc())" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "group_by_attr" @@ -919,6 +1167,10 @@ "CALL_FUNCTION", "group_by_attr(query, 'type')" ], + [ + "STORE_FAST", + "funcs" + ], [ "LOAD_GLOBAL", "sorted" @@ -975,14 +1227,30 @@ "CALL_FUNCTION", "sorted(session.query(Function.name, Function.type)\n .filter_by(file=path)\n .distinct())" ], + [ + "STORE_FAST", + "all_funcs" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "func_names" + ], [ "LOAD_FAST", "all_funcs" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1067,6 +1335,14 @@ "CALL_FUNCTION_KW", "render_template('file.html',\n funcs=funcs,\n is_ipython=path == IPYTHON_FILE_PATH,\n full_path=path,\n short_path=basename(path))" ], + [ + "LOAD_FAST", + "{row.name for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1087,6 +1363,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1107,6 +1387,10 @@ "CALL_FUNCTION", "get_calls(session, path, func_name, 200)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" @@ -1119,10 +1403,22 @@ "BINARY_SUBSCR", "query[0]" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "session" @@ -1159,6 +1455,14 @@ "BINARY_SUBSCR", "session.query(Function).filter_by(file=path, name=func_name)[0]" ], + [ + "STORE_FAST", + "func" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_GLOBAL", "render_template" @@ -1187,6 +1491,14 @@ "CALL_FUNCTION_KW", "render_template('function.html',\n func=func,\n short_path=basename(path),\n calls=calls)" ], + [ + "LOAD_FAST", + "[withattrs(Call(), **row._asdict()) for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "withattrs" @@ -1227,6 +1539,10 @@ "CALL_FUNCTION", "fix_abs_path(path)" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "get_calls" @@ -1251,6 +1567,10 @@ "BINARY_SUBSCR", "get_calls(session, path, func_name, 1)[0]" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_GLOBAL", "jsonify" @@ -1427,6 +1747,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1435,6 +1759,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "render_template" @@ -1523,6 +1851,10 @@ "CALL_METHOD", "request.environ.get('werkzeug.server.shutdown')" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_FAST", "func" @@ -1583,6 +1915,10 @@ "CALL_METHOD", "session.query(Call).filter_by(id=call_id).one()" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "call" @@ -1591,6 +1927,10 @@ "LOAD_ATTR", "call.function" ], + [ + "STORE_FAST", + "func" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1763,14 +2103,34 @@ "BINARY_SUBSCR", "session.query(*Call.basic_columns + (Function.data,))\n .join(Function)\n .filter_by(body_hash=body_hash)\n .order_by(Call.start_time.desc())[:200]" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "calls" + ], [ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "function_data_set" + ], [ "LOAD_GLOBAL", "set" @@ -1779,6 +2139,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_GLOBAL", "set" @@ -1787,10 +2151,18 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "function_data_set" ], + [ + "STORE_DEREF", + "function_data" + ], [ "LOAD_GLOBAL", "json" @@ -1807,6 +2179,14 @@ "CALL_METHOD", "json.loads(function_data)" ], + [ + "STORE_DEREF", + "function_data" + ], + [ + "STORE_FAST", + " def add(key, ranges_set):\n for node in function_data[key]:\n ranges_set.add((node['start'], node['end']))" + ], [ "LOAD_FAST", "add" @@ -1827,6 +2207,10 @@ "CALL_FUNCTION", "set()" ], + [ + "STORE_FAST", + "current_loop_ranges" + ], [ "LOAD_FAST", "add" @@ -1863,14 +2247,34 @@ "LOAD_FAST", "current_loop_ranges" ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_FAST", "ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "ranges" + ], [ "LOAD_FAST", "loop_ranges" ], + [ + "CALL_FUNCTION", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "loop_ranges" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -1907,6 +2311,14 @@ "CALL_METHOD", "DecentJSONEncoder().encode(dict(\n calls=calls, ranges=ranges, loop_ranges=loop_ranges))" ], + [ + "LOAD_FAST", + "[Call.basic_dict(withattrs(Call(), **row._asdict()))\n for row in query]" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_GLOBAL", "Call" @@ -1947,6 +2359,14 @@ "CALL_METHOD", "Call.basic_dict(withattrs(Call(), **row._asdict()))" ], + [ + "LOAD_FAST", + "{row.data for row in query}" + ], + [ + "STORE_FAST", + "row" + ], [ "LOAD_FAST", "row" @@ -1967,6 +2387,10 @@ "BINARY_SUBSCR", "function_data[key]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "ranges_set" @@ -1995,6 +2419,18 @@ "CALL_METHOD", "ranges_set.add((node['start'], node['end']))" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2011,6 +2447,18 @@ "CALL_FUNCTION_KW", "dict(start=start, end=end)" ], + [ + "LOAD_FAST", + "[dict(start=start, end=end) for start, end in loop_ranges]" + ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_GLOBAL", "dict" @@ -2039,6 +2487,10 @@ "CALL_METHOD", "request.get_json()" ], + [ + "STORE_FAST", + "hashes" + ], [ "LOAD_FAST", "session" @@ -2139,6 +2591,10 @@ "CALL_METHOD", "session.query(Function.body_hash, sqlalchemy.func.count(Call.id))\n .outerjoin(Call)\n .filter(Function.body_hash.in_(hashes))\n .group_by(Function.body_hash)" ], + [ + "STORE_FAST", + "query" + ], [ "LOAD_GLOBAL", "DecentJSONEncoder" @@ -2155,10 +2611,26 @@ "LOAD_FAST", "query" ], + [ + "CALL_FUNCTION", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], [ "CALL_METHOD", "DecentJSONEncoder().encode([\n dict(hash=h, count=count)\n for h, count in query\n ])" ], + [ + "LOAD_FAST", + "[\n dict(hash=h, count=count)\n for h, count in query\n ]" + ], + [ + "STORE_FAST", + "h" + ], + [ + "STORE_FAST", + "count" + ], [ "LOAD_GLOBAL", "dict" @@ -2231,6 +2703,10 @@ "CALL_FUNCTION_KW", "argparse.ArgumentParser(description=\"Bird's Eye: A graphical Python debugger\")" ], + [ + "STORE_FAST", + "parser" + ], [ "LOAD_FAST", "parser" @@ -2275,6 +2751,10 @@ "CALL_METHOD", "parser.parse_args(argv)" ], + [ + "STORE_FAST", + "args" + ], [ "LOAD_GLOBAL", "app" diff --git a/tests/sample_results/tests-py-3.10.json b/tests/sample_results/tests-py-3.10.json index d69f30b..3b81017 100644 --- a/tests/sample_results/tests-py-3.10.json +++ b/tests/sample_results/tests-py-3.10.json @@ -1,4 +1,60 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import tempfile" + ], + [ + "STORE_NAME", + "import time" + ], + [ + "STORE_NAME", + "import unittest" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], [ "LOAD_NAME", "unittest" @@ -7,6 +63,14 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_NAME", "unittest" @@ -15,10 +79,26 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "TestFile" @@ -35,10 +115,30 @@ "CALL_METHOD", "TestFile().test_file()" ], + [ + "STORE_NAME", + "def f():\n def g():\n pass\n\n return g" + ], + [ + "STORE_NAME", + "def lambda_maker():\n def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator\n\n @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3\n\n return foo" + ], + [ + "STORE_NAME", + "lamb" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], [ "LOAD_NAME", "Tester" @@ -47,6 +147,10 @@ "CALL_FUNCTION", "Tester()" ], + [ + "STORE_NAME", + "tester" + ], [ "LOAD_NAME", "tester" @@ -267,6 +371,14 @@ "COMPARE_OP", "tester.foo(45, False) == 45" ], + [ + "STORE_NAME", + "def empty_decorator(func):\n return func" + ], + [ + "STORE_NAME", + "def decorator_with_args(*_, **__):\n return empty_decorator" + ], [ "LOAD_NAME", "__name__" @@ -287,6 +399,98 @@ "CALL_METHOD", "unittest.main()" ], + [ + "LOAD_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + " def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])" + ], + [ + "STORE_NAME", + " def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], + [ + "STORE_NAME", + " def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" + ], + [ + "STORE_NAME", + " def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )" + ], + [ + "STORE_NAME", + " def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()" + ], + [ + "STORE_NAME", + " def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)" + ], + [ + "STORE_NAME", + " def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" + ], + [ + "STORE_NAME", + " def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]" + ], + [ + "STORE_NAME", + " def test_future_import(self):\n tester(4)" + ], + [ + "STORE_NAME", + " def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)" + ], + [ + "STORE_NAME", + " def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)" + ], + [ + "STORE_NAME", + " def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )" + ], + [ + "STORE_NAME", + " def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)" + ], + [ + "STORE_NAME", + " def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))" + ], + [ + "STORE_NAME", + " def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)" + ], + [ + "STORE_NAME", + " def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)" + ], + [ + "STORE_NAME", + " def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)" + ], + [ + "STORE_NAME", + " def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)" + ], + [ + "STORE_NAME", + " def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)" + ], + [ + "STORE_NAME", + " def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_GLOBAL", "tester" @@ -587,10 +791,22 @@ "CALL_FUNCTION", "empty_decorator" ], + [ + "STORE_FAST", + " @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], [ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(y) for y in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])" @@ -599,6 +815,14 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])" @@ -607,10 +831,18 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "LOAD_GLOBAL", "list" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in [1])" + ], [ "CALL_FUNCTION", "list(tester(x) for x in [1])" @@ -619,6 +851,14 @@ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -631,6 +871,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(y) for y in [1]}" + ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_GLOBAL", "tester" @@ -643,6 +891,14 @@ "CALL_FUNCTION", "tester(y)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -655,6 +911,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -667,6 +931,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -679,6 +951,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in [1])" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -779,6 +1059,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "LOAD_FAST", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -791,6 +1083,10 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "STORE_DEREF", + "x" + ], [ "LOAD_GLOBAL", "str" @@ -803,10 +1099,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()" + ], [ "LOAD_FAST", "foo" @@ -815,6 +1119,14 @@ "CALL_FUNCTION", "foo()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -843,6 +1155,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -871,6 +1195,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -891,6 +1227,10 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "STORE_DEREF", + "y" + ], [ "LOAD_GLOBAL", "str" @@ -903,6 +1243,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -919,6 +1263,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -935,10 +1283,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" + ], [ "LOAD_FAST", "bar" @@ -947,6 +1303,14 @@ "CALL_FUNCTION", "bar()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -975,6 +1339,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1003,6 +1379,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1023,6 +1411,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1051,6 +1447,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1079,6 +1487,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1099,6 +1519,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1135,6 +1563,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1171,6 +1611,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1199,6 +1651,10 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "STORE_DEREF", + "z" + ], [ "LOAD_GLOBAL", "str" @@ -1211,6 +1667,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1227,6 +1687,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1243,6 +1707,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1259,10 +1727,22 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1291,6 +1771,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1319,6 +1811,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1339,6 +1843,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1367,6 +1879,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1395,6 +1919,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1415,6 +1951,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1451,6 +1995,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1487,6 +2043,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1515,6 +2083,14 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1559,6 +2135,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1603,6 +2191,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1691,6 +2291,10 @@ "CALL_FUNCTION", "tester([1, 2, 3])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -1808,16 +2412,36 @@ "tester(0)" ], [ - "LOAD_GLOBAL", - "tester" + "LOAD_GLOBAL", + "tester" + ], + [ + "BINARY_TRUE_DIVIDE", + "1 / 0" + ], + [ + "CALL_FUNCTION", + "tester(1 / 0)" + ], + [ + "CALL_FUNCTION", + " with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" + ], + [ + "CALL_FUNCTION", + " with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" + ], + [ + "CALL_FUNCTION", + " with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" ], [ - "BINARY_TRUE_DIVIDE", - "1 / 0" + "CALL_FUNCTION", + " with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" ], [ - "CALL_FUNCTION", - "tester(1 / 0)" + "STORE_FAST", + " def gen():\n for x in [1, 2]:\n yield tester(x)" ], [ "LOAD_GLOBAL", @@ -1827,6 +2451,14 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "gen2" + ], [ "LOAD_GLOBAL", "list" @@ -1855,6 +2487,18 @@ "CALL_FUNCTION", "list(gen2)" ], + [ + "COMPARE_OP", + "list(gen()) == list(gen2) == [1, 2]" + ], + [ + "COMPARE_OP", + "list(gen()) == list(gen2) == [1, 2]" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1867,6 +2511,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1887,6 +2539,10 @@ "CALL_FUNCTION", "tester(4)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "time" @@ -1899,6 +2555,10 @@ "CALL_METHOD", "time.time()" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_GLOBAL", "range" @@ -1907,6 +2567,10 @@ "CALL_FUNCTION", "range(10000)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "Source" @@ -1935,6 +2599,10 @@ "LOAD_ATTR", "Source.executing(inspect.currentframe()).node" ], + [ + "STORE_FAST", + "new_node" + ], [ "LOAD_FAST", "node" @@ -1947,6 +2615,10 @@ "LOAD_FAST", "new_node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1999,6 +2671,10 @@ "CALL_METHOD", "self.assertLess(time.time() - start, 1)" ], + [ + "STORE_FAST", + " def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)" + ], [ "LOAD_FAST", "check" @@ -2075,6 +2751,10 @@ "CALL_METHOD", "source.encode(encoding)" ], + [ + "STORE_FAST", + "encoded" + ], [ "LOAD_FAST", "exception" @@ -2111,6 +2791,10 @@ "CALL_METHOD", "Source.decode_source(encoded)" ], + [ + "CALL_FUNCTION", + " with self.assertRaises(exception):\n Source.decode_source(encoded)" + ], [ "LOAD_GLOBAL", "Source" @@ -2127,6 +2811,10 @@ "CALL_METHOD", "Source.decode_source(encoded)" ], + [ + "STORE_FAST", + "decoded" + ], [ "LOAD_FAST", "matches" @@ -2251,6 +2939,10 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -2291,6 +2983,10 @@ "CALL_METHOD", "Source.for_filename(__file__).code_qualname(func.__code__)" ], + [ + "STORE_FAST", + "qualname" + ], [ "LOAD_FAST", "self" @@ -2507,6 +3203,10 @@ "CALL_FUNCTION", "lambda_maker()" ], + [ + "STORE_FAST", + "foo" + ], [ "LOAD_FAST", "self" @@ -2607,6 +3307,10 @@ "BINARY_MODULO", "'tester(6)\\n%s\\ntester(9)' % list(range(66000))" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "tempfile" @@ -2619,6 +3323,14 @@ "CALL_METHOD", "tempfile.mkstemp()" ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "compile" @@ -2635,6 +3347,10 @@ "CALL_FUNCTION", "compile(source, filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "open" @@ -2647,6 +3363,10 @@ "CALL_FUNCTION", "open(filename, 'w')" ], + [ + "STORE_FAST", + "outfile" + ], [ "LOAD_FAST", "outfile" @@ -2663,6 +3383,10 @@ "CALL_METHOD", "outfile.write(source)" ], + [ + "CALL_FUNCTION", + " with open(filename, 'w') as outfile:\n outfile.write(source)" + ], [ "LOAD_GLOBAL", "exec" @@ -2683,6 +3407,10 @@ "CALL_FUNCTION", "range(5)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "range" @@ -2695,6 +3423,14 @@ "CALL_FUNCTION", "range(n)" ], + [ + "CALL_FUNCTION", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "n" @@ -2755,6 +3491,18 @@ "CALL_FUNCTION", "only(gen)" ], + [ + "CALL_FUNCTION", + " with self.assertRaises(NotOneValueFound):\n only(gen)" + ], + [ + "LOAD_FAST", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "i" @@ -2795,6 +3543,10 @@ "CALL_METHOD", "os.path.join(os.path.dirname(__file__), 'not_code.txt', )" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "Source" @@ -2811,6 +3563,10 @@ "CALL_METHOD", "Source.for_filename(path)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -2843,6 +3599,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -2859,6 +3619,10 @@ "CALL_METHOD", "Source.executing(frame)" ], + [ + "STORE_FAST", + "executing" + ], [ "LOAD_FAST", "self" @@ -2903,6 +3667,10 @@ "CONTAINS_OP", "'pypy' not in sys.version.lower()" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "self" @@ -2943,6 +3711,14 @@ "CALL_METHOD", "executing.text_range()" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "self" @@ -2991,6 +3767,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -3091,6 +3871,22 @@ "CALL_FUNCTION", "str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" ], + [ + "LOAD_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + " def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_GLOBAL", "Source" @@ -3115,6 +3911,10 @@ "CALL_METHOD", "Source.for_frame(inspect.currentframe())" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "compile" @@ -3139,6 +3939,10 @@ "CALL_FUNCTION", "compile(source.text, source.filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "get_instructions" @@ -3151,10 +3955,22 @@ "CALL_FUNCTION", "get_instructions(code)" ], + [ + "STORE_FAST", + "instructions" + ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "instructions" ], + [ + "STORE_FAST", + "inst" + ], [ "LOAD_FAST", "inst" @@ -3175,6 +3991,10 @@ "LOAD_ATTR", "inst.starts_line" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "inst" @@ -3199,6 +4019,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "inst" @@ -3295,6 +4119,18 @@ "IS_OP", "Source.executing(frame).node is not None" ], + [ + "LOAD_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3303,10 +4139,34 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def f():\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "LOAD_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3315,6 +4175,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def g():\n pass" + ], [ "LOAD_NAME", "staticmethod" @@ -3323,6 +4187,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_FAST", + " def i():\n def j():\n pass\n\n return j" + ], [ "LOAD_FAST", "i" @@ -3331,14 +4203,26 @@ "CALL_FUNCTION", "i()" ], + [ + "STORE_FAST", + " def j():\n pass" + ], [ "LOAD_FAST", "j" ], + [ + "STORE_FAST", + " def g():\n pass" + ], [ "LOAD_FAST", "g" ], + [ + "STORE_FAST", + " def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator" + ], [ "LOAD_FAST", "assign" @@ -3351,10 +4235,18 @@ "CALL_FUNCTION", "assign(lambda: 1)" ], + [ + "STORE_FAST", + " @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3" + ], [ "LOAD_FAST", "foo" ], + [ + "STORE_FAST", + " def decorator(func):\n func.x = x\n return func" + ], [ "LOAD_FAST", "decorator" @@ -3375,18 +4267,90 @@ "LOAD_FAST", "func" ], + [ + "LOAD_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + " def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node" + ], + [ + "STORE_NAME", + " def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)" + ], + [ + "STORE_NAME", + " def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns" + ], + [ + "STORE_NAME", + " def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__pow__" + ], + [ + "STORE_NAME", + "__mul__" + ], + [ + "STORE_NAME", + "__sub__" + ], + [ + "STORE_NAME", + " def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self" + ], [ "LOAD_NAME", "__invert__" ], + [ + "STORE_NAME", + "__neg__" + ], + [ + "STORE_NAME", + "__pos__" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self" + ], [ "LOAD_NAME", "__lt__" ], + [ + "STORE_NAME", + "__ne__" + ], + [ + "STORE_NAME", + "__ge__" + ], [ "LOAD_GLOBAL", "inspect" @@ -3407,6 +4371,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -3443,6 +4411,10 @@ "LOAD_ATTR", "Source.executing(frame).node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3491,6 +4463,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "eval" @@ -3551,6 +4527,10 @@ "CALL_FUNCTION", "eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -3591,6 +4571,10 @@ "CALL_METHOD", "self.get_node(ast.Call)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "self" @@ -3683,6 +4667,10 @@ "CALL_METHOD", "self.get_node(ast.Attribute)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3747,6 +4735,10 @@ "CALL_METHOD", "self.get_node(ast.Subscript)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3823,6 +4815,10 @@ "CALL_METHOD", "self.get_node(ast.BinOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3895,6 +4891,10 @@ "CALL_METHOD", "self.get_node(ast.UnaryOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3943,6 +4943,10 @@ "CALL_METHOD", "self.get_node(ast.Compare)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" diff --git a/tests/sample_results/tests-py-3.8.json b/tests/sample_results/tests-py-3.8.json index ef28a31..c3f6c35 100644 --- a/tests/sample_results/tests-py-3.8.json +++ b/tests/sample_results/tests-py-3.8.json @@ -1,4 +1,60 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import tempfile" + ], + [ + "STORE_NAME", + "import time" + ], + [ + "STORE_NAME", + "import unittest" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], [ "LOAD_NAME", "unittest" @@ -7,6 +63,14 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_NAME", "unittest" @@ -15,10 +79,26 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "TestFile" @@ -35,10 +115,30 @@ "CALL_METHOD", "TestFile().test_file()" ], + [ + "STORE_NAME", + "def f():\n def g():\n pass\n\n return g" + ], + [ + "STORE_NAME", + "def lambda_maker():\n def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator\n\n @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3\n\n return foo" + ], + [ + "STORE_NAME", + "lamb" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], [ "LOAD_NAME", "Tester" @@ -47,6 +147,10 @@ "CALL_FUNCTION", "Tester()" ], + [ + "STORE_NAME", + "tester" + ], [ "LOAD_NAME", "tester" @@ -267,6 +371,14 @@ "COMPARE_OP", "tester.foo(45, False) == 45" ], + [ + "STORE_NAME", + "def empty_decorator(func):\n return func" + ], + [ + "STORE_NAME", + "def decorator_with_args(*_, **__):\n return empty_decorator" + ], [ "LOAD_NAME", "__name__" @@ -287,6 +399,98 @@ "CALL_METHOD", "unittest.main()" ], + [ + "LOAD_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + " def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])" + ], + [ + "STORE_NAME", + " def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], + [ + "STORE_NAME", + " def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" + ], + [ + "STORE_NAME", + " def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )" + ], + [ + "STORE_NAME", + " def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()" + ], + [ + "STORE_NAME", + " def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)" + ], + [ + "STORE_NAME", + " def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" + ], + [ + "STORE_NAME", + " def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]" + ], + [ + "STORE_NAME", + " def test_future_import(self):\n tester(4)" + ], + [ + "STORE_NAME", + " def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)" + ], + [ + "STORE_NAME", + " def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)" + ], + [ + "STORE_NAME", + " def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )" + ], + [ + "STORE_NAME", + " def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)" + ], + [ + "STORE_NAME", + " def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))" + ], + [ + "STORE_NAME", + " def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)" + ], + [ + "STORE_NAME", + " def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)" + ], + [ + "STORE_NAME", + " def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)" + ], + [ + "STORE_NAME", + " def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)" + ], + [ + "STORE_NAME", + " def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)" + ], + [ + "STORE_NAME", + " def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_GLOBAL", "tester" @@ -587,10 +791,22 @@ "CALL_FUNCTION", "empty_decorator" ], + [ + "STORE_FAST", + " @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], [ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(y) for y in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])" @@ -599,6 +815,14 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])" @@ -607,10 +831,18 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "LOAD_GLOBAL", "list" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in [1])" + ], [ "CALL_FUNCTION", "list(tester(x) for x in [1])" @@ -619,6 +851,14 @@ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -631,6 +871,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(y) for y in [1]}" + ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_GLOBAL", "tester" @@ -643,6 +891,14 @@ "CALL_FUNCTION", "tester(y)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -655,6 +911,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -667,6 +931,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -679,6 +951,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in [1])" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -779,6 +1059,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "LOAD_FAST", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -791,6 +1083,10 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "STORE_DEREF", + "x" + ], [ "LOAD_GLOBAL", "str" @@ -803,10 +1099,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()" + ], [ "LOAD_FAST", "foo" @@ -815,6 +1119,14 @@ "CALL_FUNCTION", "foo()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -843,6 +1155,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -871,6 +1195,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -891,6 +1227,10 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "STORE_DEREF", + "y" + ], [ "LOAD_GLOBAL", "str" @@ -903,6 +1243,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -919,6 +1263,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -935,10 +1283,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" + ], [ "LOAD_FAST", "bar" @@ -947,6 +1303,14 @@ "CALL_FUNCTION", "bar()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -975,6 +1339,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1003,6 +1379,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1023,6 +1411,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1051,6 +1447,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1079,6 +1487,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1099,6 +1519,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1135,6 +1563,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1171,6 +1611,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1199,6 +1651,10 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "STORE_DEREF", + "z" + ], [ "LOAD_GLOBAL", "str" @@ -1211,6 +1667,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1227,6 +1687,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1243,6 +1707,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1259,10 +1727,22 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1291,6 +1771,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1319,6 +1811,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1339,6 +1843,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1367,6 +1879,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1395,6 +1919,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1415,6 +1951,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1451,6 +1995,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1487,6 +2043,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1515,6 +2083,14 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1559,6 +2135,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1603,6 +2191,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1691,6 +2291,10 @@ "CALL_FUNCTION", "tester([1, 2, 3])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -1811,6 +2415,10 @@ "CALL_FUNCTION", "tester(1 / 0)" ], + [ + "STORE_FAST", + " def gen():\n for x in [1, 2]:\n yield tester(x)" + ], [ "LOAD_GLOBAL", "tester" @@ -1819,6 +2427,14 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "gen2" + ], [ "LOAD_GLOBAL", "list" @@ -1847,6 +2463,10 @@ "CALL_FUNCTION", "list(gen2)" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1859,6 +2479,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1879,6 +2507,10 @@ "CALL_FUNCTION", "tester(4)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "time" @@ -1891,6 +2523,10 @@ "CALL_METHOD", "time.time()" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_GLOBAL", "range" @@ -1899,6 +2535,10 @@ "CALL_FUNCTION", "range(10000)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "Source" @@ -1927,6 +2567,10 @@ "LOAD_ATTR", "Source.executing(inspect.currentframe()).node" ], + [ + "STORE_FAST", + "new_node" + ], [ "LOAD_FAST", "node" @@ -1939,6 +2583,10 @@ "LOAD_FAST", "new_node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1991,6 +2639,10 @@ "CALL_METHOD", "self.assertLess(time.time() - start, 1)" ], + [ + "STORE_FAST", + " def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)" + ], [ "LOAD_FAST", "check" @@ -2067,6 +2719,10 @@ "CALL_METHOD", "source.encode(encoding)" ], + [ + "STORE_FAST", + "encoded" + ], [ "LOAD_FAST", "exception" @@ -2119,6 +2775,10 @@ "CALL_METHOD", "Source.decode_source(encoded)" ], + [ + "STORE_FAST", + "decoded" + ], [ "LOAD_FAST", "matches" @@ -2243,6 +2903,10 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -2283,6 +2947,10 @@ "CALL_METHOD", "Source.for_filename(__file__).code_qualname(func.__code__)" ], + [ + "STORE_FAST", + "qualname" + ], [ "LOAD_FAST", "self" @@ -2499,6 +3167,10 @@ "CALL_FUNCTION", "lambda_maker()" ], + [ + "STORE_FAST", + "foo" + ], [ "LOAD_FAST", "self" @@ -2599,6 +3271,10 @@ "BINARY_MODULO", "'tester(6)\\n%s\\ntester(9)' % list(range(66000))" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "tempfile" @@ -2611,6 +3287,14 @@ "CALL_METHOD", "tempfile.mkstemp()" ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "compile" @@ -2627,6 +3311,10 @@ "CALL_FUNCTION", "compile(source, filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "open" @@ -2639,6 +3327,10 @@ "CALL_FUNCTION", "open(filename, 'w')" ], + [ + "STORE_FAST", + "outfile" + ], [ "LOAD_FAST", "outfile" @@ -2675,6 +3367,10 @@ "CALL_FUNCTION", "range(5)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "range" @@ -2687,6 +3383,14 @@ "CALL_FUNCTION", "range(n)" ], + [ + "CALL_FUNCTION", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "n" @@ -2747,6 +3451,14 @@ "CALL_FUNCTION", "only(gen)" ], + [ + "LOAD_FAST", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "i" @@ -2787,6 +3499,10 @@ "CALL_METHOD", "os.path.join(os.path.dirname(__file__), 'not_code.txt', )" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "Source" @@ -2803,6 +3519,10 @@ "CALL_METHOD", "Source.for_filename(path)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -2835,6 +3555,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -2851,6 +3575,10 @@ "CALL_METHOD", "Source.executing(frame)" ], + [ + "STORE_FAST", + "executing" + ], [ "LOAD_FAST", "self" @@ -2895,6 +3623,10 @@ "COMPARE_OP", "'pypy' not in sys.version.lower()" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "self" @@ -2935,6 +3667,14 @@ "CALL_METHOD", "executing.text_range()" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "self" @@ -2983,6 +3723,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -3083,6 +3827,22 @@ "CALL_FUNCTION", "str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" ], + [ + "LOAD_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + " def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_GLOBAL", "Source" @@ -3107,6 +3867,10 @@ "CALL_METHOD", "Source.for_frame(inspect.currentframe())" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "compile" @@ -3131,6 +3895,10 @@ "CALL_FUNCTION", "compile(source.text, source.filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "get_instructions" @@ -3143,10 +3911,22 @@ "CALL_FUNCTION", "get_instructions(code)" ], + [ + "STORE_FAST", + "instructions" + ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "instructions" ], + [ + "STORE_FAST", + "inst" + ], [ "LOAD_FAST", "inst" @@ -3167,6 +3947,10 @@ "LOAD_ATTR", "inst.starts_line" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "inst" @@ -3191,6 +3975,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "inst" @@ -3287,6 +4075,18 @@ "COMPARE_OP", "Source.executing(frame).node is not None" ], + [ + "LOAD_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3295,10 +4095,34 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def f():\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "LOAD_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3307,6 +4131,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def g():\n pass" + ], [ "LOAD_NAME", "staticmethod" @@ -3315,6 +4143,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_FAST", + " def i():\n def j():\n pass\n\n return j" + ], [ "LOAD_FAST", "i" @@ -3323,14 +4159,26 @@ "CALL_FUNCTION", "i()" ], + [ + "STORE_FAST", + " def j():\n pass" + ], [ "LOAD_FAST", "j" ], + [ + "STORE_FAST", + " def g():\n pass" + ], [ "LOAD_FAST", "g" ], + [ + "STORE_FAST", + " def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator" + ], [ "LOAD_FAST", "assign" @@ -3343,10 +4191,18 @@ "CALL_FUNCTION", "assign(lambda: 1)" ], + [ + "STORE_FAST", + " @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3" + ], [ "LOAD_FAST", "foo" ], + [ + "STORE_FAST", + " def decorator(func):\n func.x = x\n return func" + ], [ "LOAD_FAST", "decorator" @@ -3367,18 +4223,90 @@ "LOAD_FAST", "func" ], + [ + "LOAD_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + " def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node" + ], + [ + "STORE_NAME", + " def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)" + ], + [ + "STORE_NAME", + " def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns" + ], + [ + "STORE_NAME", + " def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__pow__" + ], + [ + "STORE_NAME", + "__mul__" + ], + [ + "STORE_NAME", + "__sub__" + ], + [ + "STORE_NAME", + " def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self" + ], [ "LOAD_NAME", "__invert__" ], + [ + "STORE_NAME", + "__neg__" + ], + [ + "STORE_NAME", + "__pos__" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self" + ], [ "LOAD_NAME", "__lt__" ], + [ + "STORE_NAME", + "__ne__" + ], + [ + "STORE_NAME", + "__ge__" + ], [ "LOAD_GLOBAL", "inspect" @@ -3399,6 +4327,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -3435,6 +4367,10 @@ "LOAD_ATTR", "Source.executing(frame).node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3483,6 +4419,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "eval" @@ -3543,6 +4483,10 @@ "CALL_FUNCTION", "eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -3583,6 +4527,10 @@ "CALL_METHOD", "self.get_node(ast.Call)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "self" @@ -3675,6 +4623,10 @@ "CALL_METHOD", "self.get_node(ast.Attribute)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3739,6 +4691,10 @@ "CALL_METHOD", "self.get_node(ast.Subscript)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3815,6 +4771,10 @@ "CALL_METHOD", "self.get_node(ast.BinOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3887,6 +4847,10 @@ "CALL_METHOD", "self.get_node(ast.UnaryOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3935,6 +4899,10 @@ "CALL_METHOD", "self.get_node(ast.Compare)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" diff --git a/tests/sample_results/tests-py-3.9.json b/tests/sample_results/tests-py-3.9.json index 5da1752..9fda6d5 100644 --- a/tests/sample_results/tests-py-3.9.json +++ b/tests/sample_results/tests-py-3.9.json @@ -1,4 +1,60 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import tempfile" + ], + [ + "STORE_NAME", + "import time" + ], + [ + "STORE_NAME", + "import unittest" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], + [ + "STORE_NAME", + "from executing import Source, only, PY3, NotOneValueFound, get_instructions" + ], [ "LOAD_NAME", "unittest" @@ -7,6 +63,14 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_NAME", "unittest" @@ -15,10 +79,26 @@ "LOAD_ATTR", "unittest.TestCase" ], + [ + "CALL_FUNCTION", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "TestFile" @@ -35,10 +115,30 @@ "CALL_METHOD", "TestFile().test_file()" ], + [ + "STORE_NAME", + "def f():\n def g():\n pass\n\n return g" + ], + [ + "STORE_NAME", + "def lambda_maker():\n def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator\n\n @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3\n\n return foo" + ], + [ + "STORE_NAME", + "lamb" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], [ "LOAD_NAME", "Tester" @@ -47,6 +147,10 @@ "CALL_FUNCTION", "Tester()" ], + [ + "STORE_NAME", + "tester" + ], [ "LOAD_NAME", "tester" @@ -267,6 +371,14 @@ "COMPARE_OP", "tester.foo(45, False) == 45" ], + [ + "STORE_NAME", + "def empty_decorator(func):\n return func" + ], + [ + "STORE_NAME", + "def decorator_with_args(*_, **__):\n return empty_decorator" + ], [ "LOAD_NAME", "__name__" @@ -287,6 +399,98 @@ "CALL_METHOD", "unittest.main()" ], + [ + "LOAD_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + "class TestStuff(unittest.TestCase):\n\n # noinspection PyTrailingSemicolon\n def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])\n # @formatter:on\n\n def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass\n\n def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])\n # but not if everything is the same\n # noinspection PyTypeChecker\n # with self.assertRaises((AttributeError, NotOneValueFound)):\n # str([{tester(x) for x in [1]}, {tester(x) for x in [2]}])\n\n def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )\n\n def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()\n # @formatter:on\n\n def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)\n\n def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass\n\n def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]\n\n def test_future_import(self):\n tester(4)\n\n def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)\n\n def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)\n\n def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )\n\n def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)\n\n def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))\n\n def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)\n\n def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)\n\n def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)\n\n def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)\n\n def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)\n\n def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], + [ + "STORE_NAME", + " def test_semicolons(self):\n # @formatter:off\n tester(1); tester(2); tester(3)\n tester(9\n ); tester(\n 8); tester(\n 99\n ); tester(33); tester([4,\n 5, 6, [\n 7]])" + ], + [ + "STORE_NAME", + " def test_decorator(self):\n @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], + [ + "STORE_NAME", + " def test_comprehensions(self):\n # Comprehensions can be separated if they contain different names\n str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])\n # or are on different lines\n str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])\n # or are of different types\n str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" + ], + [ + "STORE_NAME", + " def test_lambda(self):\n self.assertEqual(\n (lambda x: (tester(x), tester(x)))(tester(3)),\n (3, 3),\n )\n (lambda: (lambda: tester(1))())()\n self.assertEqual(\n (lambda: [tester(x) for x in tester([1, 2])])(),\n [1, 2],\n )" + ], + [ + "STORE_NAME", + " def test_closures_and_nested_comprehensions(self):\n x = 1\n # @formatter:off\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()\n\n foo()" + ], + [ + "STORE_NAME", + " def test_indirect_call(self):\n dict(x=tester)['x'](tester)(3, check_func=False)" + ], + [ + "STORE_NAME", + " def test_compound_statements(self):\n with self.assertRaises(TypeError):\n try:\n for _ in tester([1, 2, 3]):\n while tester(0):\n pass\n else:\n tester(4)\n else:\n tester(5)\n raise ValueError\n except tester(ValueError):\n tester(9)\n raise TypeError\n finally:\n tester(10)\n\n # PyCharm getting confused somehow?\n # noinspection PyUnreachableCode\n str()\n\n with self.assertRaises(tester(Exception)):\n if tester(0):\n pass\n elif tester(0):\n pass\n elif tester(1 / 0):\n pass" + ], + [ + "STORE_NAME", + " def test_generator(self):\n def gen():\n for x in [1, 2]:\n yield tester(x)\n\n gen2 = (tester(x) for x in tester([1, 2]))\n\n assert list(gen()) == list(gen2) == [1, 2]" + ], + [ + "STORE_NAME", + " def test_future_import(self):\n tester(4)" + ], + [ + "STORE_NAME", + " def test_many_calls(self):\n node = None\n start = time.time()\n for i in range(10000):\n new_node = Source.executing(inspect.currentframe()).node\n if node is None:\n node = new_node\n else:\n self.assertIs(node, new_node)\n self.assertLess(time.time() - start, 1)" + ], + [ + "STORE_NAME", + " def test_decode_source(self):\n def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)\n\n check(u'# coding=utf8\\n\u00e9', 'utf8')\n check(u'# coding=gbk\\n\u00e9', 'gbk')\n\n check(u'# coding=utf8\\n\u00e9', 'gbk', exception=UnicodeDecodeError)\n check(u'# coding=gbk\\n\u00e9', 'utf8', matches=False)\n\n # In Python 3 the default encoding is assumed to be UTF8\n if PY3:\n check(u'\u00e9', 'utf8')\n check(u'\u00e9', 'gbk', exception=SyntaxError)" + ], + [ + "STORE_NAME", + " def test_multiline_strings(self):\n tester('a')\n tester('''\n ab''')\n tester('''\n abc\n def\n '''\n )\n str([\n tester(\n '''\n 123\n 456\n '''\n ),\n tester(\n '''\n 345\n 456786\n '''\n ),\n ])\n tester(\n [\n '''\n 123\n 456\n '''\n '''\n 345\n 456786\n '''\n ,\n '''\n 123\n 456\n ''',\n '''\n 345\n 456786\n '''\n ]\n )" + ], + [ + "STORE_NAME", + " def test_multiple_statements_on_one_line(self):\n if tester(1): tester(2)\n for _ in tester([1, 2]): tester(3)" + ], + [ + "STORE_NAME", + " def assert_qualname(self, func, qn, check_actual_qualname=True):\n qualname = Source.for_filename(__file__).code_qualname(func.__code__)\n self.assertEqual(qn, qualname)\n if PY3 and check_actual_qualname:\n self.assertEqual(qn, func.__qualname__)\n self.assertTrue(qn.endswith(func.__name__))" + ], + [ + "STORE_NAME", + " def test_qualname(self):\n self.assert_qualname(C.f, 'C.f')\n self.assert_qualname(C.D.g, 'C.D.g')\n self.assert_qualname(f, 'f')\n self.assert_qualname(f(), 'f..g')\n self.assert_qualname(C.D.h(), 'C.D.h..i..j')\n self.assert_qualname(lamb, '')\n foo = lambda_maker()\n self.assert_qualname(foo, 'lambda_maker..foo')\n self.assert_qualname(foo.x, 'lambda_maker..')\n self.assert_qualname(foo(), 'lambda_maker..foo..')\n self.assert_qualname(foo()(), 'lambda_maker..foo..', check_actual_qualname=False)" + ], + [ + "STORE_NAME", + " def test_extended_arg(self):\n source = 'tester(6)\\n%s\\ntester(9)' % list(range(66000))\n _, filename = tempfile.mkstemp()\n code = compile(source, filename, 'exec')\n with open(filename, 'w') as outfile:\n outfile.write(source)\n exec(code)" + ], + [ + "STORE_NAME", + " def test_only(self):\n for n in range(5):\n gen = (i for i in range(n))\n if n == 1:\n self.assertEqual(only(gen), 0)\n else:\n with self.assertRaises(NotOneValueFound):\n only(gen)" + ], + [ + "STORE_NAME", + " def test_invalid_python(self):\n path = os.path.join(os.path.dirname(__file__), 'not_code.txt', )\n source = Source.for_filename(path)\n self.assertIsNone(source.tree)" + ], + [ + "STORE_NAME", + " def test_executing_methods(self):\n frame = inspect.currentframe()\n executing = Source.executing(frame)\n self.assertEqual(executing.code_qualname(), 'TestStuff.test_executing_methods')\n if 'pypy' not in sys.version.lower():\n text = 'Source.executing(frame)'\n self.assertEqual(executing.text(), text)\n start, end = executing.text_range()\n self.assertEqual(executing.source.text[start:end], text)" + ], + [ + "STORE_NAME", + " def test_attr(self):\n c = C()\n c.x = c.y = tester\n str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" + ], [ "LOAD_GLOBAL", "tester" @@ -587,10 +791,22 @@ "CALL_FUNCTION", "empty_decorator" ], + [ + "STORE_FAST", + " @empty_decorator\n @decorator_with_args(tester('123'), x=int())\n @tester(list(tuple([1, 2])), returns=empty_decorator)\n @tester(\n list(\n tuple(\n [3, 4])),\n returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(\n str(),\n x=int())\n @tester(list(tuple([5, 6])), returns=empty_decorator)\n @tester(list(tuple([7, 8])), returns=empty_decorator)\n @empty_decorator\n @decorator_with_args(tester('sdf'), x=tester('123234'))\n def foo():\n pass" + ], [ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(y) for y in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, {tester(y) for y in [1]}])" @@ -599,6 +815,14 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "CALL_FUNCTION", "str([{tester(x) for x in [1]},\n {tester(x) for x in [1]}])" @@ -607,10 +831,18 @@ "LOAD_GLOBAL", "str" ], + [ + "CALL_FUNCTION", + "{tester(x) for x in [1]}" + ], [ "LOAD_GLOBAL", "list" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in [1])" + ], [ "CALL_FUNCTION", "list(tester(x) for x in [1])" @@ -619,6 +851,14 @@ "CALL_FUNCTION", "str([{tester(x) for x in [1]}, list(tester(x) for x in [1])])" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -631,6 +871,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(y) for y in [1]}" + ], + [ + "STORE_FAST", + "y" + ], [ "LOAD_GLOBAL", "tester" @@ -643,6 +891,14 @@ "CALL_FUNCTION", "tester(y)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -655,6 +911,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -667,6 +931,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "{tester(x) for x in [1]}" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -679,6 +951,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in [1])" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -779,6 +1059,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "LOAD_FAST", + "[tester(x) for x in tester([1, 2])]" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -791,6 +1083,10 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "STORE_DEREF", + "x" + ], [ "LOAD_GLOBAL", "str" @@ -803,10 +1099,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def foo():\n y = 2\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n\n bar()" + ], [ "LOAD_FAST", "foo" @@ -815,6 +1119,14 @@ "CALL_FUNCTION", "foo()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -843,6 +1155,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -871,6 +1195,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -891,6 +1227,10 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "STORE_DEREF", + "y" + ], [ "LOAD_GLOBAL", "str" @@ -903,6 +1243,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -919,6 +1263,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -935,10 +1283,18 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "STORE_FAST", + " def bar():\n z = 3\n str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})\n str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" + ], [ "LOAD_FAST", "bar" @@ -947,6 +1303,14 @@ "CALL_FUNCTION", "bar()" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -975,6 +1339,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1003,6 +1379,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1023,6 +1411,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1051,6 +1447,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1079,6 +1487,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1099,6 +1519,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1135,6 +1563,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1171,6 +1611,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1199,6 +1651,10 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "STORE_DEREF", + "z" + ], [ "LOAD_GLOBAL", "str" @@ -1211,6 +1667,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1227,6 +1687,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1243,6 +1707,10 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" @@ -1259,10 +1727,22 @@ "CALL_FUNCTION", "tester([5, 6])" ], + [ + "CALL_FUNCTION", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], [ "CALL_FUNCTION", "str({tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])})" ], + [ + "LOAD_FAST", + "{tester(a+x): {tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1291,6 +1771,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x): {tester(c+x) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1319,6 +1811,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1339,6 +1843,14 @@ "CALL_FUNCTION", "tester(c+x)" ], + [ + "LOAD_FAST", + "{tester(a+y): {tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1367,6 +1879,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+y): {tester(c+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1395,6 +1919,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1415,6 +1951,14 @@ "CALL_FUNCTION", "tester(c+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y): {tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1451,6 +1995,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y): {tester(c+x+y) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1487,6 +2043,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1515,6 +2083,14 @@ "CALL_FUNCTION", "tester(c+x+y)" ], + [ + "LOAD_FAST", + "{tester(a+x+y+z): {tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])} for a in tester([5, 6])}" + ], + [ + "STORE_FAST", + "a" + ], [ "LOAD_GLOBAL", "tester" @@ -1559,6 +2135,18 @@ "CALL_FUNCTION", "tester([3, 4])" ], + [ + "CALL_FUNCTION", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "LOAD_FAST", + "{tester(b+x+y+z): {tester(c+x+y+z) for c in tester([1, 2])} for b in tester([3, 4])}" + ], + [ + "STORE_FAST", + "b" + ], [ "LOAD_GLOBAL", "tester" @@ -1603,6 +2191,18 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "LOAD_FAST", + "{tester(c+x+y+z) for c in tester([1, 2])}" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -1691,6 +2291,10 @@ "CALL_FUNCTION", "tester([1, 2, 3])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -1751,14 +2355,6 @@ "CALL_FUNCTION", "tester(10)" ], - [ - "LOAD_GLOBAL", - "tester" - ], - [ - "CALL_FUNCTION", - "tester(10)" - ], [ "LOAD_GLOBAL", "str" @@ -1819,6 +2415,10 @@ "CALL_FUNCTION", "tester(1 / 0)" ], + [ + "STORE_FAST", + " def gen():\n for x in [1, 2]:\n yield tester(x)" + ], [ "LOAD_GLOBAL", "tester" @@ -1827,6 +2427,14 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "CALL_FUNCTION", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "gen2" + ], [ "LOAD_GLOBAL", "list" @@ -1855,6 +2463,10 @@ "CALL_FUNCTION", "list(gen2)" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1867,6 +2479,14 @@ "CALL_FUNCTION", "tester(x)" ], + [ + "LOAD_FAST", + "(tester(x) for x in tester([1, 2]))" + ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tester" @@ -1887,6 +2507,10 @@ "CALL_FUNCTION", "tester(4)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "time" @@ -1899,6 +2523,10 @@ "CALL_METHOD", "time.time()" ], + [ + "STORE_FAST", + "start" + ], [ "LOAD_GLOBAL", "range" @@ -1907,6 +2535,10 @@ "CALL_FUNCTION", "range(10000)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "Source" @@ -1935,6 +2567,10 @@ "LOAD_ATTR", "Source.executing(inspect.currentframe()).node" ], + [ + "STORE_FAST", + "new_node" + ], [ "LOAD_FAST", "node" @@ -1947,6 +2583,10 @@ "LOAD_FAST", "new_node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1999,6 +2639,10 @@ "CALL_METHOD", "self.assertLess(time.time() - start, 1)" ], + [ + "STORE_FAST", + " def check(source, encoding, exception=None, matches=True):\n encoded = source.encode(encoding)\n if exception:\n with self.assertRaises(exception):\n Source.decode_source(encoded)\n else:\n decoded = Source.decode_source(encoded)\n if matches:\n self.assertEqual(decoded, source)\n else:\n self.assertNotEqual(decoded, source)" + ], [ "LOAD_FAST", "check" @@ -2075,6 +2719,10 @@ "CALL_METHOD", "source.encode(encoding)" ], + [ + "STORE_FAST", + "encoded" + ], [ "LOAD_FAST", "exception" @@ -2127,6 +2775,10 @@ "CALL_METHOD", "Source.decode_source(encoded)" ], + [ + "STORE_FAST", + "decoded" + ], [ "LOAD_FAST", "matches" @@ -2251,6 +2903,10 @@ "CALL_FUNCTION", "tester([1, 2])" ], + [ + "STORE_FAST", + "_" + ], [ "LOAD_GLOBAL", "tester" @@ -2291,6 +2947,10 @@ "CALL_METHOD", "Source.for_filename(__file__).code_qualname(func.__code__)" ], + [ + "STORE_FAST", + "qualname" + ], [ "LOAD_FAST", "self" @@ -2507,6 +3167,10 @@ "CALL_FUNCTION", "lambda_maker()" ], + [ + "STORE_FAST", + "foo" + ], [ "LOAD_FAST", "self" @@ -2607,6 +3271,10 @@ "BINARY_MODULO", "'tester(6)\\n%s\\ntester(9)' % list(range(66000))" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "tempfile" @@ -2619,6 +3287,14 @@ "CALL_METHOD", "tempfile.mkstemp()" ], + [ + "STORE_FAST", + "_" + ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "compile" @@ -2635,6 +3311,10 @@ "CALL_FUNCTION", "compile(source, filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "open" @@ -2647,6 +3327,10 @@ "CALL_FUNCTION", "open(filename, 'w')" ], + [ + "STORE_FAST", + "outfile" + ], [ "LOAD_FAST", "outfile" @@ -2683,6 +3367,10 @@ "CALL_FUNCTION", "range(5)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_GLOBAL", "range" @@ -2695,6 +3383,14 @@ "CALL_FUNCTION", "range(n)" ], + [ + "CALL_FUNCTION", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "n" @@ -2755,6 +3451,14 @@ "CALL_FUNCTION", "only(gen)" ], + [ + "LOAD_FAST", + "(i for i in range(n))" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "i" @@ -2795,6 +3499,10 @@ "CALL_METHOD", "os.path.join(os.path.dirname(__file__), 'not_code.txt', )" ], + [ + "STORE_FAST", + "path" + ], [ "LOAD_GLOBAL", "Source" @@ -2811,6 +3519,10 @@ "CALL_METHOD", "Source.for_filename(path)" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_FAST", "self" @@ -2843,6 +3555,10 @@ "CALL_METHOD", "inspect.currentframe()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -2859,6 +3575,10 @@ "CALL_METHOD", "Source.executing(frame)" ], + [ + "STORE_FAST", + "executing" + ], [ "LOAD_FAST", "self" @@ -2903,6 +3623,10 @@ "CONTAINS_OP", "'pypy' not in sys.version.lower()" ], + [ + "STORE_FAST", + "text" + ], [ "LOAD_FAST", "self" @@ -2943,6 +3667,14 @@ "CALL_METHOD", "executing.text_range()" ], + [ + "STORE_FAST", + "start" + ], + [ + "STORE_FAST", + "end" + ], [ "LOAD_FAST", "self" @@ -2991,6 +3723,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "tester" @@ -3091,6 +3827,22 @@ "CALL_FUNCTION", "str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe))" ], + [ + "LOAD_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + "class TestFile(unittest.TestCase):\n def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], + [ + "STORE_NAME", + " def test_file(self):\n source = Source.for_frame(inspect.currentframe())\n code = compile(source.text, source.filename, 'exec')\n instructions = get_instructions(code)\n lineno = None\n for inst in instructions:\n if inst.starts_line is not None:\n lineno = inst.starts_line\n if not inst.opname.startswith(\n ('BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'COMPARE_OP')):\n continue\n frame = C()\n frame.f_lasti = inst.offset\n frame.f_code = code\n frame.f_globals = globals()\n frame.f_lineno = lineno\n print(inst.opname)\n assert Source.executing(frame).node is not None" + ], [ "LOAD_GLOBAL", "Source" @@ -3115,6 +3867,10 @@ "CALL_METHOD", "Source.for_frame(inspect.currentframe())" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "compile" @@ -3139,6 +3895,10 @@ "CALL_FUNCTION", "compile(source.text, source.filename, 'exec')" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_GLOBAL", "get_instructions" @@ -3151,10 +3911,22 @@ "CALL_FUNCTION", "get_instructions(code)" ], + [ + "STORE_FAST", + "instructions" + ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "instructions" ], + [ + "STORE_FAST", + "inst" + ], [ "LOAD_FAST", "inst" @@ -3175,6 +3947,10 @@ "LOAD_ATTR", "inst.starts_line" ], + [ + "STORE_FAST", + "lineno" + ], [ "LOAD_FAST", "inst" @@ -3199,6 +3975,10 @@ "CALL_FUNCTION", "C()" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "inst" @@ -3295,6 +4075,18 @@ "IS_OP", "Source.executing(frame).node is not None" ], + [ + "LOAD_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + "class C(object):\n @staticmethod\n def f():\n pass\n\n class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3303,10 +4095,34 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def f():\n pass" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "LOAD_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_NAME", + " class D(object):\n @staticmethod\n def g():\n pass\n\n @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], [ "LOAD_NAME", "staticmethod" @@ -3315,6 +4131,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def g():\n pass" + ], [ "LOAD_NAME", "staticmethod" @@ -3323,6 +4143,14 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def h():\n def i():\n def j():\n pass\n\n return j\n\n return i()" + ], + [ + "STORE_FAST", + " def i():\n def j():\n pass\n\n return j" + ], [ "LOAD_FAST", "i" @@ -3331,14 +4159,26 @@ "CALL_FUNCTION", "i()" ], + [ + "STORE_FAST", + " def j():\n pass" + ], [ "LOAD_FAST", "j" ], + [ + "STORE_FAST", + " def g():\n pass" + ], [ "LOAD_FAST", "g" ], + [ + "STORE_FAST", + " def assign(x):\n def decorator(func):\n func.x = x\n return func\n\n return decorator" + ], [ "LOAD_FAST", "assign" @@ -3351,10 +4191,18 @@ "CALL_FUNCTION", "assign(lambda: 1)" ], + [ + "STORE_FAST", + " @assign(lambda: 1)\n def foo():\n return lambda: lambda: 3" + ], [ "LOAD_FAST", "foo" ], + [ + "STORE_FAST", + " def decorator(func):\n func.x = x\n return func" + ], [ "LOAD_FAST", "decorator" @@ -3375,18 +4223,90 @@ "LOAD_FAST", "func" ], + [ + "LOAD_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + "class Tester(object):\n def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node\n\n def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)\n\n def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns\n\n def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self\n\n def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self\n\n def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self\n\n __pow__ = __mul__ = __sub__ = __add__\n\n def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self\n\n __neg__ = __pos__ = __invert__\n\n def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self\n\n __ne__ = __ge__ = __lt__" + ], + [ + "STORE_NAME", + " def get_node(self, typ):\n frame = inspect.currentframe().f_back.f_back\n Source.lazycache(frame)\n node = Source.executing(frame).node\n assert isinstance(node, typ), (node, typ)\n return node" + ], + [ + "STORE_NAME", + " def check(self, node, value):\n frame = inspect.currentframe().f_back.f_back\n result = eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )\n assert result == value, (result, value)" + ], + [ + "STORE_NAME", + " def __call__(self, arg, check_func=True, returns=None):\n call = self.get_node(ast.Call)\n self.check(call.args[0], arg)\n if check_func:\n self.check(call.func, self)\n if returns is None:\n return arg\n return returns" + ], + [ + "STORE_NAME", + " def __getattr__(self, item):\n node = self.get_node(ast.Attribute)\n self.check(node.value, self)\n assert node.attr == item\n return self" + ], + [ + "STORE_NAME", + " def __getitem__(self, item):\n node = self.get_node(ast.Subscript)\n self.check(node.value, self)\n self.check(node.slice.value, item)\n return self" + ], + [ + "STORE_NAME", + " def __add__(self, other):\n node = self.get_node(ast.BinOp)\n self.check(node.left, self)\n self.check(node.right, other)\n return self" + ], [ "LOAD_NAME", "__add__" ], + [ + "STORE_NAME", + "__pow__" + ], + [ + "STORE_NAME", + "__mul__" + ], + [ + "STORE_NAME", + "__sub__" + ], + [ + "STORE_NAME", + " def __invert__(self):\n node = self.get_node(ast.UnaryOp)\n self.check(node.operand, self)\n return self" + ], [ "LOAD_NAME", "__invert__" ], + [ + "STORE_NAME", + "__neg__" + ], + [ + "STORE_NAME", + "__pos__" + ], + [ + "STORE_NAME", + " def __lt__(self, other):\n node = self.get_node(ast.Compare)\n self.check(node.left, self)\n self.check(node.comparators[0], other)\n return self" + ], [ "LOAD_NAME", "__lt__" ], + [ + "STORE_NAME", + "__ne__" + ], + [ + "STORE_NAME", + "__ge__" + ], [ "LOAD_GLOBAL", "inspect" @@ -3407,6 +4327,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "Source" @@ -3443,6 +4367,10 @@ "LOAD_ATTR", "Source.executing(frame).node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3491,6 +4419,10 @@ "LOAD_ATTR", "inspect.currentframe().f_back.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "eval" @@ -3551,6 +4483,10 @@ "CALL_FUNCTION", "eval(\n compile(ast.Expression(node), frame.f_code.co_filename, 'eval'),\n frame.f_globals,\n frame.f_locals,\n )" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -3591,6 +4527,10 @@ "CALL_METHOD", "self.get_node(ast.Call)" ], + [ + "STORE_FAST", + "call" + ], [ "LOAD_FAST", "self" @@ -3683,6 +4623,10 @@ "CALL_METHOD", "self.get_node(ast.Attribute)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3747,6 +4691,10 @@ "CALL_METHOD", "self.get_node(ast.Subscript)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3823,6 +4771,10 @@ "CALL_METHOD", "self.get_node(ast.BinOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3895,6 +4847,10 @@ "CALL_METHOD", "self.get_node(ast.UnaryOp)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -3943,6 +4899,10 @@ "CALL_METHOD", "self.get_node(ast.Compare)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" diff --git a/tests/sample_results/tracer-py-3.10.json b/tests/sample_results/tracer-py-3.10.json index 4e249ad..1e75fd3 100644 --- a/tests/sample_results/tracer-py-3.10.json +++ b/tests/sample_results/tracer-py-3.10.json @@ -1,4 +1,24 @@ [ + [ + "STORE_NAME", + "\"\"\"\nThis module provides the generic functionality of tracing code by\nmodifying its AST. Eventually this will become a separate package.\nThis is similar to the standard library module bdb, while birdseye\nitself would correspond to pdb.\nMost of the work is in TreeTracerBase.\n\"\"\"" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,14 +31,154 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from copy import deepcopy" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from itertools import takewhile" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_NAME", "NamedTuple" @@ -75,6 +235,10 @@ "CALL_FUNCTION", "NamedTuple('EnterCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call begins\n ('enter_node', ast.AST),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType)])" ], + [ + "STORE_NAME", + "EnterCallInfo" + ], [ "LOAD_NAME", "NamedTuple" @@ -167,6 +331,10 @@ "CALL_FUNCTION", "NamedTuple('ExitCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call explicitly returned\n ('return_node', Optional[ast.Return]),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType),\n\n # Node where the call explicitly returned\n ('return_value', Any),\n\n # Exception raised in the call causing it to end,\n # will propagate to the caller\n ('exc_value', Optional[Exception]),\n\n # Traceback corresponding to exc_value\n ('exc_tb', Optional[TracebackType])])" ], + [ + "STORE_NAME", + "ExitCallInfo" + ], [ "LOAD_NAME", "namedtuple" @@ -175,10 +343,22 @@ "CALL_FUNCTION", "namedtuple('ChangeValue', 'value')" ], + [ + "STORE_NAME", + "ChangeValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_NAME", "ast" @@ -187,10 +367,30 @@ "LOAD_ATTR", "ast.NodeTransformer" ], + [ + "CALL_FUNCTION", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "def ancestors(node):\n # type: (ast.AST) -> Iterator[ast.AST]\n while True:\n try:\n node = node.parent\n except AttributeError:\n break\n yield node" + ], [ "LOAD_NAME", "Union" @@ -223,6 +423,46 @@ "BINARY_SUBSCR", "Union[ast.For, ast.While, ast.comprehension]" ], + [ + "STORE_NAME", + "Loop" + ], + [ + "STORE_NAME", + "def loops(node):\n # type: (ast.AST) -> Tuple[Loop, ...]\n \"\"\"\n Return all the 'enclosing loops' of a node, up to the innermost class or\n function definition. This also includes the 'for in' clauses in list/dict/set/generator\n comprehensions. So for example, in this code:\n\n for x in ...:\n def foo():\n while True:\n print([z for y in ...])\n\n The loops enclosing the node 'z' are 'while True' and 'for y in ...', in that order.\n \"\"\"\n result = []\n while True:\n try:\n parent = node.parent\n except AttributeError:\n break\n if isinstance(parent, ast.FunctionDef):\n break\n\n is_containing_loop = (((isinstance(parent, ast.For) and parent.iter is not node or\n isinstance(parent, ast.While))\n and node not in parent.orelse) or\n (isinstance(parent, ast.comprehension) and node in parent.ifs))\n if is_containing_loop:\n result.append(parent)\n\n elif isinstance(parent, (ast.ListComp,\n ast.GeneratorExp,\n ast.DictComp,\n ast.SetComp)):\n generators = parent.generators\n if node in generators:\n generators = list(takewhile(lambda n: n != node, generators))\n result.extend(reversed(generators))\n\n node = parent\n\n result.reverse()\n return tuple(result)" + ], + [ + "LOAD_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "\"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"" + ], + [ + "STORE_NAME", + "is_ipython_cell" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename" + ], + [ + "STORE_NAME", + " def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True" + ], + [ + "STORE_NAME", + " def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_GLOBAL", "compile" @@ -311,6 +551,10 @@ "CALL_METHOD", "tracer.parse_extra(self.root, source, filename)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_FAST", "new_root" @@ -371,6 +615,10 @@ "CALL_FUNCTION", "deepcopy(self.root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "_NodeVisitor" @@ -391,6 +639,10 @@ "CALL_METHOD", "_NodeVisitor().visit(new_root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "compile" @@ -483,6 +735,10 @@ "CALL_METHOD", "ast.walk(self.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +755,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -575,6 +835,14 @@ "CALL_FUNCTION", "enumerate(self.root.body)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -611,6 +879,10 @@ "BINARY_SUBSCR", "self.root.body[:i + 1]" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "ast" @@ -627,6 +899,10 @@ "CALL_METHOD", "ast.walk(s)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -643,6 +919,10 @@ "LOAD_ATTR", "self.nodes" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -679,6 +959,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -699,6 +983,26 @@ "STORE_ATTR", "stmt._enter_call_node" ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "\"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_FAST", "self" @@ -739,6 +1043,26 @@ "STORE_ATTR", "self.exc_value" ], + [ + "LOAD_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}" + ], [ "LOAD_NAME", "lru_cache" @@ -751,6 +1075,78 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)" + ], + [ + "STORE_NAME", + " def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func" + ], + [ + "STORE_NAME", + " def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator" + ], + [ + "STORE_NAME", + " def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value" + ], + [ + "STORE_NAME", + " def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)" + ], + [ + "STORE_NAME", + " def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))" + ], + [ + "STORE_NAME", + " def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_FAST", "self" @@ -835,6 +1231,18 @@ "LOAD_ATTR", "self._treetrace_hidden_after_expr" ], + [ + "CALL_FUNCTION", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "LOAD_FAST", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -963,6 +1371,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -975,6 +1387,14 @@ "CALL_FUNCTION", "is_ipython_cell(filename)" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], + [ + "STORE_FAST", + "import linecache" + ], [ "LOAD_FAST", "get_ipython" @@ -991,6 +1411,10 @@ "LOAD_ATTR", "get_ipython().compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_METHOD", "''.join" @@ -1019,6 +1443,10 @@ "CALL_METHOD", "''.join(linecache.cache[filename][2])" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "read_source_file" @@ -1031,6 +1459,14 @@ "CALL_FUNCTION", "read_source_file(filename)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -1055,6 +1491,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_DEREF", "func" @@ -1071,6 +1511,14 @@ "CALL_FUNCTION", "ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')" ], + [ + "STORE_DEREF", + "code_options" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)" + ], [ "LOAD_DEREF", "find_code" @@ -1131,6 +1579,10 @@ "BINARY_SUBSCR", "code_options[0]" ], + [ + "STORE_FAST", + "new_func_code" + ], [ "LOAD_DEREF", "func" @@ -1207,6 +1659,10 @@ "CALL_FUNCTION", "FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_GLOBAL", "update_wrapper" @@ -1271,6 +1727,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -1335,6 +1795,10 @@ "COMPARE_OP", "const.co_name == func.__code__.co_name" ], + [ + "STORE_FAST", + "matches" + ], [ "LOAD_FAST", "matches" @@ -1423,6 +1887,10 @@ "LOAD_ATTR", "self.trace_function" ], + [ + "STORE_FAST", + " def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "decorator" @@ -1443,6 +1911,10 @@ "CALL_METHOD", "self.trace_function(actual_func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "wraps" @@ -1459,6 +1931,10 @@ "CALL_FUNCTION", "wraps(actual_func)" ], + [ + "STORE_FAST", + " @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -1475,6 +1951,10 @@ "CALL_METHOD", "kwargs.pop('trace_call', False)" ], + [ + "STORE_FAST", + "trace_call" + ], [ "LOAD_FAST", "trace_call" @@ -1483,10 +1963,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_DEREF", "actual_func" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -1515,6 +2003,10 @@ "CALL_METHOD", "sys._getframe(2)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "self" @@ -1535,6 +2027,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(frame)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1547,6 +2043,10 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "original_frame" + ], [ "LOAD_FAST", "frame" @@ -1571,6 +2071,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1599,6 +2103,10 @@ "CALL_FUNCTION", "ancestors(node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1655,6 +2163,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1739,6 +2251,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1759,6 +2275,10 @@ "CALL_FUNCTION", "cast(ast.stmt, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1775,6 +2295,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "_StmtContext" @@ -1811,6 +2335,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1831,6 +2359,10 @@ "CALL_FUNCTION", "cast(ast.expr, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1847,6 +2379,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1875,6 +2411,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -1935,6 +2475,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1971,6 +2515,10 @@ "CALL_METHOD", "self._after_expr(node, frame, value, None, None)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -2003,6 +2551,10 @@ "LOAD_ATTR", "result.value" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -2023,6 +2575,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2107,6 +2663,14 @@ "CALL_METHOD", "self._get_caller_stuff(current_frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_GLOBAL", "FrameInfo" @@ -2175,6 +2739,14 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "self" @@ -2195,6 +2767,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(caller_frame)" ], + [ + "STORE_FAST", + "main_frame" + ], [ "LOAD_FAST", "main_frame" @@ -2203,6 +2779,10 @@ "LOAD_FAST", "main_frame" ], + [ + "STORE_FAST", + "caller_frame" + ], [ "LOAD_FAST", "self" @@ -2219,6 +2799,10 @@ "BINARY_SUBSCR", "self.stack[caller_frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2227,6 +2811,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -2239,6 +2827,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "caller_frame" @@ -2259,6 +2851,10 @@ "BINARY_SUBSCR", "frame_info.statement_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "caller_frame" @@ -2267,6 +2863,34 @@ "LOAD_FAST", "call_node" ], + [ + "LOAD_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "\"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"" + ], + [ + "STORE_NAME", + " def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)" + ], + [ + "STORE_NAME", + " def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker" + ], + [ + "STORE_NAME", + " def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped" + ], [ "LOAD_NAME", "staticmethod" @@ -2275,6 +2899,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_GLOBAL", "getattr" @@ -2471,6 +3099,10 @@ "CALL_METHOD", "self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)" ], + [ + "STORE_FAST", + "before_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2571,6 +3203,10 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )" ], + [ + "STORE_FAST", + "after_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2659,6 +3295,10 @@ "CALL_METHOD", "self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)" ], + [ + "STORE_FAST", + "context_expr" + ], [ "LOAD_GLOBAL", "PY3" @@ -2695,6 +3335,10 @@ "CALL_FUNCTION_KW", "ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2715,6 +3359,10 @@ "CALL_FUNCTION_KW", "ast.With(\n context_expr=context_expr,\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2819,6 +3467,34 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" ], + [ + "LOAD_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame" + ], + [ + "STORE_NAME", + " def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], [ "LOAD_FAST", "tracer" @@ -2863,6 +3539,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2871,6 +3551,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2879,6 +3563,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "getattr" @@ -2927,6 +3615,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2983,6 +3675,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2991,6 +3687,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2999,6 +3699,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "tracer" @@ -3015,6 +3719,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -3031,6 +3739,10 @@ "CALL_METHOD", "frame_info.statement_stack.pop()" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3055,6 +3767,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3075,6 +3791,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -3087,6 +3807,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "tracer" @@ -3147,6 +3871,10 @@ "CALL_METHOD", "tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3187,6 +3915,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_FAST", "frame_info" @@ -3195,6 +3927,10 @@ "LOAD_ATTR", "frame_info.return_node" ], + [ + "STORE_FAST", + "return_node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3251,6 +3987,10 @@ "LOAD_FAST", "return_node" ], + [ + "STORE_FAST", + "exiting" + ], [ "LOAD_FAST", "exiting" @@ -3271,6 +4011,18 @@ "CALL_METHOD", "tracer._get_caller_stuff(frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "return_node" @@ -3307,6 +4059,10 @@ "BINARY_SUBSCR", "frame_info.expression_values[return_node.value]" ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "tracer" @@ -3367,6 +4123,10 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "tracer.stack[frame]" + ], [ "LOAD_FAST", "self" @@ -3391,6 +4151,10 @@ "CALL_METHOD", "self.tracer.main_to_secondary_frames.pop(frame)" ], + [ + "STORE_FAST", + "secondary_frame" + ], [ "LOAD_FAST", "self" @@ -3407,6 +4171,10 @@ "LOAD_FAST", "secondary_frame" ], + [ + "DELETE_SUBSCR", + "self.tracer.secondary_to_main_frames[secondary_frame]" + ], [ "LOAD_FAST", "result" @@ -3419,6 +4187,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3427,6 +4199,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "node" @@ -3435,6 +4211,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3567,6 +4347,10 @@ "CONTAINS_OP", "node in parent.ifs" ], + [ + "STORE_FAST", + "is_containing_loop" + ], [ "LOAD_FAST", "is_containing_loop" @@ -3639,6 +4423,10 @@ "LOAD_ATTR", "parent.generators" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_DEREF", "node" @@ -3671,6 +4459,10 @@ "CALL_FUNCTION", "list(takewhile(lambda n: n != node, generators))" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_FAST", "result" @@ -3699,6 +4491,10 @@ "LOAD_FAST", "parent" ], + [ + "STORE_DEREF", + "node" + ], [ "LOAD_FAST", "result" diff --git a/tests/sample_results/tracer-py-3.8.json b/tests/sample_results/tracer-py-3.8.json index 27c64e7..52bd75c 100644 --- a/tests/sample_results/tracer-py-3.8.json +++ b/tests/sample_results/tracer-py-3.8.json @@ -1,4 +1,24 @@ [ + [ + "STORE_NAME", + "\"\"\"\nThis module provides the generic functionality of tracing code by\nmodifying its AST. Eventually this will become a separate package.\nThis is similar to the standard library module bdb, while birdseye\nitself would correspond to pdb.\nMost of the work is in TreeTracerBase.\n\"\"\"" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,14 +31,154 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from copy import deepcopy" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from itertools import takewhile" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_NAME", "NamedTuple" @@ -75,6 +235,10 @@ "CALL_FUNCTION", "NamedTuple('EnterCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call begins\n ('enter_node', ast.AST),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType)])" ], + [ + "STORE_NAME", + "EnterCallInfo" + ], [ "LOAD_NAME", "NamedTuple" @@ -167,6 +331,10 @@ "CALL_FUNCTION", "NamedTuple('ExitCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call explicitly returned\n ('return_node', Optional[ast.Return]),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType),\n\n # Node where the call explicitly returned\n ('return_value', Any),\n\n # Exception raised in the call causing it to end,\n # will propagate to the caller\n ('exc_value', Optional[Exception]),\n\n # Traceback corresponding to exc_value\n ('exc_tb', Optional[TracebackType])])" ], + [ + "STORE_NAME", + "ExitCallInfo" + ], [ "LOAD_NAME", "namedtuple" @@ -175,10 +343,22 @@ "CALL_FUNCTION", "namedtuple('ChangeValue', 'value')" ], + [ + "STORE_NAME", + "ChangeValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_NAME", "ast" @@ -187,10 +367,30 @@ "LOAD_ATTR", "ast.NodeTransformer" ], + [ + "CALL_FUNCTION", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "def ancestors(node):\n # type: (ast.AST) -> Iterator[ast.AST]\n while True:\n try:\n node = node.parent\n except AttributeError:\n break\n yield node" + ], [ "LOAD_NAME", "Union" @@ -223,6 +423,46 @@ "BINARY_SUBSCR", "Union[ast.For, ast.While, ast.comprehension]" ], + [ + "STORE_NAME", + "Loop" + ], + [ + "STORE_NAME", + "def loops(node):\n # type: (ast.AST) -> Tuple[Loop, ...]\n \"\"\"\n Return all the 'enclosing loops' of a node, up to the innermost class or\n function definition. This also includes the 'for in' clauses in list/dict/set/generator\n comprehensions. So for example, in this code:\n\n for x in ...:\n def foo():\n while True:\n print([z for y in ...])\n\n The loops enclosing the node 'z' are 'while True' and 'for y in ...', in that order.\n \"\"\"\n result = []\n while True:\n try:\n parent = node.parent\n except AttributeError:\n break\n if isinstance(parent, ast.FunctionDef):\n break\n\n is_containing_loop = (((isinstance(parent, ast.For) and parent.iter is not node or\n isinstance(parent, ast.While))\n and node not in parent.orelse) or\n (isinstance(parent, ast.comprehension) and node in parent.ifs))\n if is_containing_loop:\n result.append(parent)\n\n elif isinstance(parent, (ast.ListComp,\n ast.GeneratorExp,\n ast.DictComp,\n ast.SetComp)):\n generators = parent.generators\n if node in generators:\n generators = list(takewhile(lambda n: n != node, generators))\n result.extend(reversed(generators))\n\n node = parent\n\n result.reverse()\n return tuple(result)" + ], + [ + "LOAD_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "\"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"" + ], + [ + "STORE_NAME", + "is_ipython_cell" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename" + ], + [ + "STORE_NAME", + " def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True" + ], + [ + "STORE_NAME", + " def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_GLOBAL", "compile" @@ -311,6 +551,10 @@ "CALL_METHOD", "tracer.parse_extra(self.root, source, filename)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_FAST", "new_root" @@ -371,6 +615,10 @@ "CALL_FUNCTION", "deepcopy(self.root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "_NodeVisitor" @@ -391,6 +639,10 @@ "CALL_METHOD", "_NodeVisitor().visit(new_root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "compile" @@ -483,6 +735,10 @@ "CALL_METHOD", "ast.walk(self.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +755,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -575,6 +835,14 @@ "CALL_FUNCTION", "enumerate(self.root.body)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -611,6 +879,10 @@ "BINARY_SUBSCR", "self.root.body[:i + 1]" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "ast" @@ -627,6 +899,10 @@ "CALL_METHOD", "ast.walk(s)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -643,6 +919,10 @@ "LOAD_ATTR", "self.nodes" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -679,6 +959,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -699,6 +983,26 @@ "STORE_ATTR", "stmt._enter_call_node" ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "\"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_FAST", "self" @@ -739,6 +1043,26 @@ "STORE_ATTR", "self.exc_value" ], + [ + "LOAD_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}" + ], [ "LOAD_NAME", "lru_cache" @@ -751,6 +1075,78 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)" + ], + [ + "STORE_NAME", + " def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func" + ], + [ + "STORE_NAME", + " def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator" + ], + [ + "STORE_NAME", + " def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value" + ], + [ + "STORE_NAME", + " def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)" + ], + [ + "STORE_NAME", + " def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))" + ], + [ + "STORE_NAME", + " def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_FAST", "self" @@ -835,6 +1231,18 @@ "LOAD_ATTR", "self._treetrace_hidden_after_expr" ], + [ + "CALL_FUNCTION", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "LOAD_FAST", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -963,6 +1371,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -975,6 +1387,14 @@ "CALL_FUNCTION", "is_ipython_cell(filename)" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], + [ + "STORE_FAST", + "import linecache" + ], [ "LOAD_FAST", "get_ipython" @@ -991,6 +1411,10 @@ "LOAD_ATTR", "get_ipython().compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_METHOD", "''.join" @@ -1019,6 +1443,10 @@ "CALL_METHOD", "''.join(linecache.cache[filename][2])" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "read_source_file" @@ -1031,6 +1459,14 @@ "CALL_FUNCTION", "read_source_file(filename)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -1055,6 +1491,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_DEREF", "func" @@ -1071,6 +1511,14 @@ "CALL_FUNCTION", "ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')" ], + [ + "STORE_DEREF", + "code_options" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)" + ], [ "LOAD_DEREF", "find_code" @@ -1131,6 +1579,10 @@ "BINARY_SUBSCR", "code_options[0]" ], + [ + "STORE_FAST", + "new_func_code" + ], [ "LOAD_DEREF", "func" @@ -1207,6 +1659,10 @@ "CALL_FUNCTION", "FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_GLOBAL", "update_wrapper" @@ -1271,6 +1727,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -1335,6 +1795,10 @@ "COMPARE_OP", "const.co_name == func.__code__.co_name" ], + [ + "STORE_FAST", + "matches" + ], [ "LOAD_FAST", "matches" @@ -1423,6 +1887,10 @@ "LOAD_ATTR", "self.trace_function" ], + [ + "STORE_FAST", + " def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "decorator" @@ -1443,6 +1911,10 @@ "CALL_METHOD", "self.trace_function(actual_func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "wraps" @@ -1459,6 +1931,10 @@ "CALL_FUNCTION", "wraps(actual_func)" ], + [ + "STORE_FAST", + " @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -1475,6 +1951,10 @@ "CALL_METHOD", "kwargs.pop('trace_call', False)" ], + [ + "STORE_FAST", + "trace_call" + ], [ "LOAD_FAST", "trace_call" @@ -1483,10 +1963,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_DEREF", "actual_func" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -1515,6 +2003,10 @@ "CALL_METHOD", "sys._getframe(2)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "self" @@ -1535,6 +2027,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(frame)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1547,6 +2043,10 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "original_frame" + ], [ "LOAD_FAST", "frame" @@ -1571,6 +2071,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "ancestors" @@ -1583,6 +2087,10 @@ "CALL_FUNCTION", "ancestors(node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1639,6 +2147,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1723,6 +2235,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1743,6 +2259,10 @@ "CALL_FUNCTION", "cast(ast.stmt, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1759,6 +2279,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "_StmtContext" @@ -1795,6 +2319,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1815,6 +2343,10 @@ "CALL_FUNCTION", "cast(ast.expr, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1831,6 +2363,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1859,6 +2395,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -1919,6 +2459,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1955,6 +2499,10 @@ "CALL_METHOD", "self._after_expr(node, frame, value, None, None)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1987,6 +2535,10 @@ "LOAD_ATTR", "result.value" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -2007,6 +2559,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2091,6 +2647,14 @@ "CALL_METHOD", "self._get_caller_stuff(current_frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_GLOBAL", "FrameInfo" @@ -2159,6 +2723,14 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "self" @@ -2179,6 +2751,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(caller_frame)" ], + [ + "STORE_FAST", + "main_frame" + ], [ "LOAD_FAST", "main_frame" @@ -2187,6 +2763,10 @@ "LOAD_FAST", "main_frame" ], + [ + "STORE_FAST", + "caller_frame" + ], [ "LOAD_FAST", "self" @@ -2203,6 +2783,10 @@ "BINARY_SUBSCR", "self.stack[caller_frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2211,6 +2795,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -2223,6 +2811,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "frame_info" @@ -2235,6 +2827,10 @@ "BINARY_SUBSCR", "frame_info.statement_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "caller_frame" @@ -2243,6 +2839,34 @@ "LOAD_FAST", "call_node" ], + [ + "LOAD_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "\"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"" + ], + [ + "STORE_NAME", + " def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)" + ], + [ + "STORE_NAME", + " def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker" + ], + [ + "STORE_NAME", + " def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped" + ], [ "LOAD_NAME", "staticmethod" @@ -2251,6 +2875,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_GLOBAL", "getattr" @@ -2447,6 +3075,10 @@ "CALL_METHOD", "self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)" ], + [ + "STORE_FAST", + "before_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2547,6 +3179,10 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )" ], + [ + "STORE_FAST", + "after_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2635,6 +3271,10 @@ "CALL_METHOD", "self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)" ], + [ + "STORE_FAST", + "context_expr" + ], [ "LOAD_GLOBAL", "PY3" @@ -2671,6 +3311,10 @@ "CALL_FUNCTION_KW", "ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2691,6 +3335,10 @@ "CALL_FUNCTION_KW", "ast.With(\n context_expr=context_expr,\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2795,6 +3443,34 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" ], + [ + "LOAD_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame" + ], + [ + "STORE_NAME", + " def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], [ "LOAD_FAST", "tracer" @@ -2839,6 +3515,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2847,6 +3527,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2855,6 +3539,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "getattr" @@ -2903,6 +3591,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2959,6 +3651,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2967,6 +3663,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2975,6 +3675,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "tracer" @@ -2991,6 +3695,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -3007,6 +3715,10 @@ "CALL_METHOD", "frame_info.statement_stack.pop()" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3031,6 +3743,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3051,6 +3767,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -3063,6 +3783,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "tracer" @@ -3123,6 +3847,10 @@ "CALL_METHOD", "tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3163,6 +3891,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_FAST", "frame_info" @@ -3171,6 +3903,10 @@ "LOAD_ATTR", "frame_info.return_node" ], + [ + "STORE_FAST", + "return_node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3227,6 +3963,10 @@ "LOAD_FAST", "return_node" ], + [ + "STORE_FAST", + "exiting" + ], [ "LOAD_FAST", "exiting" @@ -3247,6 +3987,18 @@ "CALL_METHOD", "tracer._get_caller_stuff(frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "return_node" @@ -3283,6 +4035,10 @@ "BINARY_SUBSCR", "frame_info.expression_values[return_node.value]" ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "tracer" @@ -3343,6 +4099,10 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "tracer.stack[frame]" + ], [ "LOAD_FAST", "self" @@ -3367,6 +4127,10 @@ "CALL_METHOD", "self.tracer.main_to_secondary_frames.pop(frame)" ], + [ + "STORE_FAST", + "secondary_frame" + ], [ "LOAD_FAST", "self" @@ -3383,6 +4147,10 @@ "LOAD_FAST", "secondary_frame" ], + [ + "DELETE_SUBSCR", + "self.tracer.secondary_to_main_frames[secondary_frame]" + ], [ "LOAD_FAST", "result" @@ -3395,6 +4163,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3403,6 +4175,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "node" @@ -3411,6 +4187,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3543,6 +4323,10 @@ "COMPARE_OP", "node in parent.ifs" ], + [ + "STORE_FAST", + "is_containing_loop" + ], [ "LOAD_FAST", "is_containing_loop" @@ -3615,6 +4399,10 @@ "LOAD_ATTR", "parent.generators" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_DEREF", "node" @@ -3647,6 +4435,10 @@ "CALL_FUNCTION", "list(takewhile(lambda n: n != node, generators))" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_FAST", "result" @@ -3675,6 +4467,10 @@ "LOAD_FAST", "parent" ], + [ + "STORE_DEREF", + "node" + ], [ "LOAD_FAST", "result" diff --git a/tests/sample_results/tracer-py-3.9.json b/tests/sample_results/tracer-py-3.9.json index 1fc178e..98274e9 100644 --- a/tests/sample_results/tracer-py-3.9.json +++ b/tests/sample_results/tracer-py-3.9.json @@ -1,4 +1,24 @@ [ + [ + "STORE_NAME", + "\"\"\"\nThis module provides the generic functionality of tracing code by\nmodifying its AST. Eventually this will become a separate package.\nThis is similar to the standard library module bdb, while birdseye\nitself would correspond to pdb.\nMost of the work is in TreeTracerBase.\n\"\"\"" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,14 +31,154 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from collections import namedtuple, defaultdict" + ], + [ + "STORE_NAME", + "from copy import deepcopy" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from functools import partial, update_wrapper, wraps" + ], + [ + "STORE_NAME", + "from itertools import takewhile" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from typing import List, Dict, Any, Optional, NamedTuple, Tuple, Iterator, Callable, cast, Union" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from types import FrameType, TracebackType, CodeType, FunctionType" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], + [ + "STORE_NAME", + "from birdseye.utils import PY3, Type, is_lambda, lru_cache, read_source_file, is_ipython_cell, \\\n is_future_import" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_NAME", "NamedTuple" @@ -75,6 +235,10 @@ "CALL_FUNCTION", "NamedTuple('EnterCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call begins\n ('enter_node', ast.AST),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType)])" ], + [ + "STORE_NAME", + "EnterCallInfo" + ], [ "LOAD_NAME", "NamedTuple" @@ -167,6 +331,10 @@ "CALL_FUNCTION", "NamedTuple('ExitCallInfo', [\n\n # Node from where the call was made\n ('call_node', Optional[Union[ast.expr, ast.stmt]]),\n\n # Node where the call explicitly returned\n ('return_node', Optional[ast.Return]),\n\n # Frame from which the call was made\n ('caller_frame', FrameType),\n\n # Frame of the call\n ('current_frame', FrameType),\n\n # Node where the call explicitly returned\n ('return_value', Any),\n\n # Exception raised in the call causing it to end,\n # will propagate to the caller\n ('exc_value', Optional[Exception]),\n\n # Traceback corresponding to exc_value\n ('exc_tb', Optional[TracebackType])])" ], + [ + "STORE_NAME", + "ExitCallInfo" + ], [ "LOAD_NAME", "namedtuple" @@ -175,10 +343,22 @@ "CALL_FUNCTION", "namedtuple('ChangeValue', 'value')" ], + [ + "STORE_NAME", + "ChangeValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_NAME", "ast" @@ -187,10 +367,30 @@ "LOAD_ATTR", "ast.NodeTransformer" ], + [ + "CALL_FUNCTION", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "def ancestors(node):\n # type: (ast.AST) -> Iterator[ast.AST]\n while True:\n try:\n node = node.parent\n except AttributeError:\n break\n yield node" + ], [ "LOAD_NAME", "Union" @@ -223,6 +423,46 @@ "BINARY_SUBSCR", "Union[ast.For, ast.While, ast.comprehension]" ], + [ + "STORE_NAME", + "Loop" + ], + [ + "STORE_NAME", + "def loops(node):\n # type: (ast.AST) -> Tuple[Loop, ...]\n \"\"\"\n Return all the 'enclosing loops' of a node, up to the innermost class or\n function definition. This also includes the 'for in' clauses in list/dict/set/generator\n comprehensions. So for example, in this code:\n\n for x in ...:\n def foo():\n while True:\n print([z for y in ...])\n\n The loops enclosing the node 'z' are 'while True' and 'for y in ...', in that order.\n \"\"\"\n result = []\n while True:\n try:\n parent = node.parent\n except AttributeError:\n break\n if isinstance(parent, ast.FunctionDef):\n break\n\n is_containing_loop = (((isinstance(parent, ast.For) and parent.iter is not node or\n isinstance(parent, ast.While))\n and node not in parent.orelse) or\n (isinstance(parent, ast.comprehension) and node in parent.ifs))\n if is_containing_loop:\n result.append(parent)\n\n elif isinstance(parent, (ast.ListComp,\n ast.GeneratorExp,\n ast.DictComp,\n ast.SetComp)):\n generators = parent.generators\n if node in generators:\n generators = list(takewhile(lambda n: n != node, generators))\n result.extend(reversed(generators))\n\n node = parent\n\n result.reverse()\n return tuple(result)" + ], + [ + "LOAD_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "class TracedFile(object):\n \"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"\n\n is_ipython_cell = False\n\n def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename\n\n def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True\n\n def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], + [ + "STORE_NAME", + "\"\"\"\n An instance of this class corresponds to a single .py file.\n It contains some useful data in the following attributes:\n\n - filename: name of the source file\n - source: textual contents of the file\n - root: root of the original Abstract Syntax Tree (AST) of the source,\n where the nodes of this tree have an additional handy attribute:\n - parent: parent of the node, so this node is a child node of its parent\n - tracer: instance of TreeTracerBase\n - code: executable code object compiled from the modified AST\n \"\"\"" + ], + [ + "STORE_NAME", + "is_ipython_cell" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, source, filename, flags):\n # type: (TreeTracerBase, str, str, int) -> None\n # Here the source code is parsed, modified, and compiled\n self.root = compile(source, filename, 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit=True) # type: ast.Module\n\n self.nodes = [] # type: List[ast.AST]\n\n self.set_basic_node_attributes()\n\n new_root = tracer.parse_extra(self.root, source, filename)\n if new_root is not None:\n self.root = new_root\n\n self.set_basic_node_attributes()\n self.set_enter_call_nodes()\n\n new_root = deepcopy(self.root)\n new_root = _NodeVisitor().visit(new_root)\n\n self.code = compile(new_root, filename, \"exec\", dont_inherit=True, flags=flags) # type: CodeType\n self.tracer = tracer\n self.source = source\n self.filename = filename" + ], + [ + "STORE_NAME", + " def set_basic_node_attributes(self):\n self.nodes = [] # type: List[ast.AST]\n for node in ast.walk(self.root): # type: ast.AST\n for child in ast.iter_child_nodes(node):\n child.parent = node\n node._tree_index = len(self.nodes)\n self.nodes.append(node)\n\n # Mark __future__ imports and anything before (i.e. module docstrings)\n # to be ignored by the AST transformer\n for i, stmt in enumerate(self.root.body):\n if is_future_import(stmt):\n for s in self.root.body[:i + 1]:\n for node in ast.walk(s):\n node._visit_ignore = True" + ], + [ + "STORE_NAME", + " def set_enter_call_nodes(self):\n for node in self.nodes:\n if isinstance(node, (ast.Module, ast.FunctionDef)):\n for stmt in node.body:\n if not is_future_import(stmt):\n stmt._enter_call_node = True\n break" + ], [ "LOAD_GLOBAL", "compile" @@ -311,6 +551,10 @@ "CALL_METHOD", "tracer.parse_extra(self.root, source, filename)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_FAST", "new_root" @@ -371,6 +615,10 @@ "CALL_FUNCTION", "deepcopy(self.root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "_NodeVisitor" @@ -391,6 +639,10 @@ "CALL_METHOD", "_NodeVisitor().visit(new_root)" ], + [ + "STORE_FAST", + "new_root" + ], [ "LOAD_GLOBAL", "compile" @@ -483,6 +735,10 @@ "CALL_METHOD", "ast.walk(self.root)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "ast" @@ -499,6 +755,10 @@ "CALL_METHOD", "ast.iter_child_nodes(node)" ], + [ + "STORE_FAST", + "child" + ], [ "LOAD_FAST", "node" @@ -575,6 +835,14 @@ "CALL_FUNCTION", "enumerate(self.root.body)" ], + [ + "STORE_FAST", + "i" + ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -611,6 +879,10 @@ "BINARY_SUBSCR", "self.root.body[:i + 1]" ], + [ + "STORE_FAST", + "s" + ], [ "LOAD_GLOBAL", "ast" @@ -627,6 +899,10 @@ "CALL_METHOD", "ast.walk(s)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "node" @@ -643,6 +919,10 @@ "LOAD_ATTR", "self.nodes" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -679,6 +959,10 @@ "LOAD_ATTR", "node.body" ], + [ + "STORE_FAST", + "stmt" + ], [ "LOAD_GLOBAL", "is_future_import" @@ -699,6 +983,26 @@ "STORE_ATTR", "stmt._enter_call_node" ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n \"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"\n def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], + [ + "STORE_NAME", + "\"\"\"\n Contains extra data about an execution frame.\n Can be obtained from the stack attribute of a TreeTracerBase instance\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Stack of statements currently being executed\n self.statement_stack = [] # type: List[ast.stmt]\n\n # Stack of expression nodes within the above statement that\n # the interpreter is planning on evaluating, or has just evaluated\n # in the case of the last element of the list. For example, given\n # the expression f(g(x)), the stack would be [f, g, x] before and just\n # after evaluating x, since function arguments are evaluated before the\n # actual function call.\n self.expression_stack = [] # type: List[ast.expr]\n\n # Mapping from the expression node to its most recent value\n # in the corresponding frame\n self.expression_values = {} # type: Dict[ast.expr, Any]\n\n # Node where the frame has explicitly returned\n # There may be parent nodes such as enclosing loops that still need to finish executing\n self.return_node = None # type: Optional[ast.Return]\n\n # Most recent exception raised in the frame\n self.exc_value = None" + ], [ "LOAD_FAST", "self" @@ -739,6 +1043,26 @@ "STORE_ATTR", "self.exc_value" ], + [ + "LOAD_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "class TreeTracerBase(object):\n \"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"\n\n def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}\n\n @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)\n\n def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}\n\n def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func\n\n def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator\n\n def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame\n\n def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)\n\n def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node\n\n def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value\n\n def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)\n\n def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))\n\n def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node\n\n # The methods below are hooks meant to be overridden in subclasses to take custom actions\n\n def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"\n\n def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"\n\n def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"\n\n def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"\n\n def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"\n\n def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"\n\n def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], + [ + "STORE_NAME", + "\"\"\"\n Create a subclass of this class with one or more of the 'hooks'\n (methods which are empty in this class) overridden to take a custom action\n in the given situation. Then decorate functions with an instance of this class\n to trace them.\n \"\"\"" + ], + [ + "STORE_NAME", + " def __init__(self):\n # Mapping from frames of execution being traced to FrameInfo objects\n # for extra metadata.\n self.stack = {} # type: Dict[FrameType, FrameInfo]\n self.main_to_secondary_frames = defaultdict(list)\n self.secondary_to_main_frames = {}" + ], [ "LOAD_NAME", "lru_cache" @@ -751,6 +1075,78 @@ "CALL_FUNCTION", "lru_cache()" ], + [ + "STORE_NAME", + " @lru_cache()\n def compile(self, source, filename, flags=0):\n # type: (str, str, int) -> TracedFile\n return TracedFile(self, source, filename, flags)" + ], + [ + "STORE_NAME", + " def _trace_methods_dict(self, traced_file):\n # type: (TracedFile) -> Dict[str, Callable]\n return {f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_NAME", + " def trace_function(self, func):\n # type: (FunctionType) -> FunctionType\n \"\"\"\n Returns a version of the passed function with the AST modified to\n trigger the tracing hooks.\n \"\"\"\n if not isinstance(func, FunctionType):\n raise ValueError('You can only trace user-defined functions. '\n 'The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n try:\n if inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func):\n raise ValueError('You cannot trace async functions')\n except AttributeError:\n pass\n\n if is_lambda(func):\n raise ValueError('You cannot trace lambdas')\n\n filename = inspect.getsourcefile(func) # type: str\n\n if is_ipython_cell(filename):\n # noinspection PyPackageRequirements\n from IPython import get_ipython\n import linecache\n\n flags = get_ipython().compile.flags\n source = ''.join(linecache.cache[filename][2])\n else:\n source = read_source_file(filename)\n flags = 0\n\n # We compile the entire file instead of just the function source\n # because it can contain context which affects the function code,\n # e.g. enclosing functions and classes or __future__ imports\n traced_file = self.compile(source, filename, flags)\n\n if func.__dict__:\n raise ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')\n\n # Then we have to recursively search through the newly compiled\n # code to find the code we actually want corresponding to this function\n code_options = [] # type: List[CodeType]\n\n def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)\n\n find_code(traced_file.code)\n\n if len(code_options) > 1:\n # Currently lambdas aren't allowed anyway, but should be in the future\n assert is_lambda(func)\n raise ValueError(\"Failed to trace lambda. Convert the function to a def.\")\n new_func_code = code_options[0] # type: CodeType\n\n # Give the new function access to the hooks\n # We have to use the original __globals__ and not a copy\n # because it's the actual module namespace that may get updated by other code\n func.__globals__.update(self._trace_methods_dict(traced_file))\n\n # http://stackoverflow.com/a/13503277/2482744\n # noinspection PyArgumentList\n new_func = FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)\n update_wrapper(new_func, func) # type: FunctionType\n if PY3:\n new_func.__kwdefaults__ = getattr(func, '__kwdefaults__', None)\n new_func.traced_file = traced_file\n return new_func" + ], + [ + "STORE_NAME", + " def __call__(self, func=None, optional=False):\n # type: (FunctionType, bool) -> Callable\n \"\"\"\n Decorator which returns a (possibly optionally) traced function.\n This decorator can be called with or without arguments.\n Typically it is called without arguments, in which case it returns\n a traced function.\n If optional=True, it returns a function similar to the original\n but with an additional optional parameter trace_call, default False.\n If trace_call is false, the underlying untraced function is used.\n If true, the traced version is used.\n \"\"\"\n if inspect.isclass(func):\n raise TypeError('Decorating classes is no longer supported')\n\n if func:\n # The decorator has been called without arguments/parentheses,\n # e.g.\n # @eye\n # def ...\n return self.trace_function(func)\n\n # The decorator has been called with arguments/parentheses,\n # e.g.\n # @eye(...)\n # def ...\n # We must return a decorator\n\n if not optional:\n return self.trace_function\n\n def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper\n\n return decorator" + ], + [ + "STORE_NAME", + " def _main_frame(self, node):\n # type: (ast.AST) -> Optional[FrameType]\n frame = sys._getframe(2)\n result = self.secondary_to_main_frames.get(frame)\n if result:\n return result\n\n original_frame = frame\n\n while frame.f_code.co_name in ('', '', ''):\n frame = frame.f_back\n\n for node in ancestors(node):\n if isinstance(node, (ast.FunctionDef, ast.Lambda)):\n break\n\n if isinstance(node, ast.ClassDef):\n frame = frame.f_back\n\n if frame.f_code.co_name in ('', ''):\n return None\n\n self.secondary_to_main_frames[original_frame] = frame\n self.main_to_secondary_frames[frame].append(original_frame)\n return frame" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_with_stmt(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> _StmtContext\n \"\"\"\n Called directly from the modified code.\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(...):\n \n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.stmt, node)\n frame = self._main_frame(node)\n return _StmtContext(self, node, frame)" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_before_expr(self, traced_file, _tree_index):\n # type: (TracedFile, int) -> ast.expr\n \"\"\"\n Called directly from the modified code before an expression is\n evaluated.\n \"\"\"\n node = traced_file.nodes[_tree_index]\n node = cast(ast.expr, node)\n frame = self._main_frame(node)\n if frame is None:\n return node\n\n frame_info = self.stack[frame]\n frame_info.expression_stack.append(node)\n\n self.before_expr(node, frame)\n return node" + ], + [ + "STORE_NAME", + " def _treetrace_hidden_after_expr(self, _, node, value):\n # type: (TracedFile, ast.expr, Any) -> Any\n \"\"\"\n Called directly from the modified code after an expression is\n evaluated.\n \"\"\"\n frame = self._main_frame(node)\n if frame is None:\n return value\n\n result = self._after_expr(node, frame, value, None, None)\n if result is not None:\n assert isinstance(result, ChangeValue), \"after_expr must return None or an instance of ChangeValue\"\n value = result.value\n return value" + ], + [ + "STORE_NAME", + " def _after_expr(self, node, frame, value, exc_value, exc_tb):\n frame_info = self.stack[frame]\n frame_info.expression_stack.pop()\n frame_info.expression_values[node] = value\n return self.after_expr(node, frame, value, exc_value, exc_tb)" + ], + [ + "STORE_NAME", + " def _enter_call(self, enter_node, current_frame):\n # type: (ast.AST, FrameType) -> None\n caller_frame, call_node = self._get_caller_stuff(current_frame)\n self.stack[current_frame] = FrameInfo()\n self.enter_call(EnterCallInfo(call_node, enter_node, caller_frame, current_frame))" + ], + [ + "STORE_NAME", + " def _get_caller_stuff(self, frame):\n # type: (FrameType) -> Tuple[FrameType, Optional[Union[ast.expr, ast.stmt]]]\n caller_frame = frame.f_back\n call_node = None\n main_frame = self.secondary_to_main_frames.get(caller_frame)\n if main_frame:\n caller_frame = main_frame\n frame_info = self.stack[caller_frame]\n expression_stack = frame_info.expression_stack\n if expression_stack:\n call_node = expression_stack[-1]\n else:\n call_node = frame_info.statement_stack[-1] # type: ignore\n return caller_frame, call_node" + ], + [ + "STORE_NAME", + " def before_expr(self, node, frame):\n # type: (ast.expr, FrameType) -> None\n \"\"\"\n Called right before the expression corresponding to `node` is evaluated\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_expr(self, node, frame, value, exc_value, exc_tb):\n # type: (ast.expr, FrameType, Any, Optional[BaseException], Optional[TracebackType]) -> Optional[ChangeValue]\n \"\"\"\n Called right after the expression corresponding to `node` is evaluated\n within `frame`. `value` is the value of the expression, if it succeeded.\n If the evaluation raised an exception, exc_value will be the exception object\n and exc_tb the traceback.\n\n Return `ChangeValue(x)` to change the value of the expression as\n seen by the rest of the program from `value` to `x`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def before_stmt(self, node, frame):\n # type: (ast.stmt, FrameType) -> None\n \"\"\"\n Called right before the statement corresponding to `node` is executed\n within `frame`.\n \"\"\"" + ], + [ + "STORE_NAME", + " def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node):\n # type: (ast.stmt, FrameType, Optional[BaseException], Optional[TracebackType], Optional[ast.AST]) -> Optional[bool]\n \"\"\"\n Called right after the statement corresponding to `node` is executed\n within `frame`.\n If the statement raised an exception, exc_value will be the exception object,\n exc_tb the traceback, and exc_node the node where the exception was raised\n (either this statement or an expression within).\n\n Returning True will suppress any exception raised (as with __exit__ in general).\n \"\"\"" + ], + [ + "STORE_NAME", + " def enter_call(self, enter_info):\n # type: (EnterCallInfo) -> None\n \"\"\"\n Called before a function call begins executing. For typical `def` functions,\n this is called before the `before_stmt` for to the first statement in the function.\n \"\"\"" + ], + [ + "STORE_NAME", + " def exit_call(self, exit_info):\n # type: (ExitCallInfo) -> None\n \"\"\"\n Called after a function call finishes executing. For typical `def` functions,\n this is called after the `after_stmt` for to the last statement to execute.\n \"\"\"" + ], + [ + "STORE_NAME", + " def parse_extra(self, root, source, filename):\n # type: (ast.Module, str, str) -> Optional[ast.Module]\n \"\"\"\n Called before the AST (root) is modified to let subclasses make additional changes first.\n \"\"\"" + ], [ "LOAD_FAST", "self" @@ -835,6 +1231,18 @@ "LOAD_ATTR", "self._treetrace_hidden_after_expr" ], + [ + "CALL_FUNCTION", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "LOAD_FAST", + "{f.__name__: partial(f, traced_file)\n for f in [\n self._treetrace_hidden_with_stmt,\n self._treetrace_hidden_before_expr,\n self._treetrace_hidden_after_expr,\n ]}" + ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -963,6 +1371,10 @@ "CALL_METHOD", "inspect.getsourcefile(func)" ], + [ + "STORE_FAST", + "filename" + ], [ "LOAD_GLOBAL", "is_ipython_cell" @@ -975,6 +1387,14 @@ "CALL_FUNCTION", "is_ipython_cell(filename)" ], + [ + "STORE_FAST", + "from IPython import get_ipython" + ], + [ + "STORE_FAST", + "import linecache" + ], [ "LOAD_FAST", "get_ipython" @@ -991,6 +1411,10 @@ "LOAD_ATTR", "get_ipython().compile.flags" ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_METHOD", "''.join" @@ -1019,6 +1443,10 @@ "CALL_METHOD", "''.join(linecache.cache[filename][2])" ], + [ + "STORE_FAST", + "source" + ], [ "LOAD_GLOBAL", "read_source_file" @@ -1031,6 +1459,14 @@ "CALL_FUNCTION", "read_source_file(filename)" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "flags" + ], [ "LOAD_FAST", "self" @@ -1055,6 +1491,10 @@ "CALL_METHOD", "self.compile(source, filename, flags)" ], + [ + "STORE_FAST", + "traced_file" + ], [ "LOAD_DEREF", "func" @@ -1071,6 +1511,14 @@ "CALL_FUNCTION", "ValueError('The birdseye decorator must be applied first, '\n 'at the bottom of the list.')" ], + [ + "STORE_DEREF", + "code_options" + ], + [ + "STORE_DEREF", + " def find_code(root_code):\n # type: (CodeType) -> None\n for const in root_code.co_consts: # type: CodeType\n if not inspect.iscode(const):\n continue\n matches = (const.co_firstlineno == func.__code__.co_firstlineno and\n const.co_name == func.__code__.co_name)\n if matches:\n code_options.append(const)\n find_code(const)" + ], [ "LOAD_DEREF", "find_code" @@ -1131,6 +1579,10 @@ "BINARY_SUBSCR", "code_options[0]" ], + [ + "STORE_FAST", + "new_func_code" + ], [ "LOAD_DEREF", "func" @@ -1207,6 +1659,10 @@ "CALL_FUNCTION", "FunctionType(new_func_code, func.__globals__, func.__name__, func.__defaults__, func.__closure__)" ], + [ + "STORE_FAST", + "new_func" + ], [ "LOAD_GLOBAL", "update_wrapper" @@ -1271,6 +1727,10 @@ "LOAD_ATTR", "root_code.co_consts" ], + [ + "STORE_FAST", + "const" + ], [ "LOAD_GLOBAL", "inspect" @@ -1335,6 +1795,10 @@ "COMPARE_OP", "const.co_name == func.__code__.co_name" ], + [ + "STORE_FAST", + "matches" + ], [ "LOAD_FAST", "matches" @@ -1423,6 +1887,10 @@ "LOAD_ATTR", "self.trace_function" ], + [ + "STORE_FAST", + " def decorator(actual_func):\n\n traced = self.trace_function(actual_func)\n\n @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "decorator" @@ -1443,6 +1911,10 @@ "CALL_METHOD", "self.trace_function(actual_func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "wraps" @@ -1459,6 +1931,10 @@ "CALL_FUNCTION", "wraps(actual_func)" ], + [ + "STORE_FAST", + " @wraps(actual_func)\n def wrapper(*args, **kwargs):\n trace_call = kwargs.pop('trace_call', False)\n if trace_call:\n f = traced\n else:\n f = actual_func\n return f(*args, **kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -1475,6 +1951,10 @@ "CALL_METHOD", "kwargs.pop('trace_call', False)" ], + [ + "STORE_FAST", + "trace_call" + ], [ "LOAD_FAST", "trace_call" @@ -1483,10 +1963,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_DEREF", "actual_func" ], + [ + "STORE_FAST", + "f" + ], [ "LOAD_FAST", "f" @@ -1515,6 +2003,10 @@ "CALL_METHOD", "sys._getframe(2)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "self" @@ -1535,6 +2027,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(frame)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1547,6 +2043,10 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "original_frame" + ], [ "LOAD_FAST", "frame" @@ -1571,6 +2071,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "ancestors" @@ -1583,6 +2087,10 @@ "CALL_FUNCTION", "ancestors(node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1639,6 +2147,10 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1723,6 +2235,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1743,6 +2259,10 @@ "CALL_FUNCTION", "cast(ast.stmt, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1759,6 +2279,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "_StmtContext" @@ -1795,6 +2319,10 @@ "BINARY_SUBSCR", "traced_file.nodes[_tree_index]" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "cast" @@ -1815,6 +2343,10 @@ "CALL_FUNCTION", "cast(ast.expr, node)" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -1831,6 +2363,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1859,6 +2395,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -1919,6 +2459,10 @@ "CALL_METHOD", "self._main_frame(node)" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -1955,6 +2499,10 @@ "CALL_METHOD", "self._after_expr(node, frame, value, None, None)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1987,6 +2535,10 @@ "LOAD_ATTR", "result.value" ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "value" @@ -2007,6 +2559,10 @@ "BINARY_SUBSCR", "self.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2091,6 +2647,14 @@ "CALL_METHOD", "self._get_caller_stuff(current_frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_GLOBAL", "FrameInfo" @@ -2159,6 +2723,14 @@ "LOAD_ATTR", "frame.f_back" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "self" @@ -2179,6 +2751,10 @@ "CALL_METHOD", "self.secondary_to_main_frames.get(caller_frame)" ], + [ + "STORE_FAST", + "main_frame" + ], [ "LOAD_FAST", "main_frame" @@ -2187,6 +2763,10 @@ "LOAD_FAST", "main_frame" ], + [ + "STORE_FAST", + "caller_frame" + ], [ "LOAD_FAST", "self" @@ -2203,6 +2783,10 @@ "BINARY_SUBSCR", "self.stack[caller_frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2211,6 +2795,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -2223,6 +2811,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "frame_info" @@ -2235,6 +2827,10 @@ "BINARY_SUBSCR", "frame_info.statement_stack[-1]" ], + [ + "STORE_FAST", + "call_node" + ], [ "LOAD_FAST", "caller_frame" @@ -2243,6 +2839,34 @@ "LOAD_FAST", "call_node" ], + [ + "LOAD_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "class _NodeVisitor(ast.NodeTransformer):\n \"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"\n\n def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)\n\n def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker\n\n def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped\n\n @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], + [ + "STORE_NAME", + "\"\"\"\n This does the AST modifications that call the hooks.\n \"\"\"" + ], + [ + "STORE_NAME", + " def generic_visit(self, node):\n # type: (ast.AST) -> ast.AST\n if not getattr(node, '_visit_ignore', False):\n if (isinstance(node, ast.expr) and\n not (hasattr(node, \"ctx\") and not isinstance(node.ctx, ast.Load)) and\n not isinstance(node, getattr(ast, 'Starred', ()))):\n return self.visit_expr(node)\n if isinstance(node, ast.stmt):\n return self.visit_stmt(node)\n return super(_NodeVisitor, self).generic_visit(node)" + ], + [ + "STORE_NAME", + " def visit_expr(self, node):\n # type: (ast.expr) -> ast.Call\n \"\"\"\n each expression e gets wrapped like this:\n _treetrace_hidden_after_expr(_treetrace_hidden_before_expr(_tree_index), e)\n\n where the _treetrace_* functions are the corresponding methods with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n\n before_marker = self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)\n ast.copy_location(before_marker, node)\n\n after_marker = ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )\n ast.copy_location(after_marker, node)\n ast.fix_missing_locations(after_marker)\n\n return after_marker" + ], + [ + "STORE_NAME", + " def visit_stmt(self, node):\n # type: (ast.stmt) -> ast.With\n \"\"\"\n Every statement in the original code becomes:\n\n with _treetrace_hidden_with_stmt(_tree_index):\n \n\n where the _treetrace_hidden_with_stmt function is the the corresponding method with the\n TreeTracerBase and traced_file arguments already filled in (see _trace_methods_dict)\n \"\"\"\n context_expr = self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)\n\n if PY3:\n wrapped = ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )\n else:\n wrapped = ast.With(\n context_expr=context_expr,\n body=[node],\n )\n ast.copy_location(wrapped, node)\n ast.fix_missing_locations(wrapped)\n return wrapped" + ], [ "LOAD_NAME", "staticmethod" @@ -2251,6 +2875,10 @@ "CALL_FUNCTION", "staticmethod" ], + [ + "STORE_NAME", + " @staticmethod\n def _create_simple_marker_call(node, func):\n # type: (ast.AST, Callable) -> ast.Call\n \"\"\"\n Returns a Call node representing `func(node._tree_index)`\n where node._tree_index is a numerical literal which allows the node object\n to be retrieved later through the nodes attribute of a TracedFile.\n \"\"\"\n return ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" + ], [ "LOAD_GLOBAL", "getattr" @@ -2447,6 +3075,10 @@ "CALL_METHOD", "self._create_simple_marker_call(node, TreeTracerBase._treetrace_hidden_before_expr)" ], + [ + "STORE_FAST", + "before_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2547,6 +3179,10 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=TreeTracerBase._treetrace_hidden_after_expr.__name__,\n ctx=ast.Load()),\n args=[\n before_marker,\n super(_NodeVisitor, self).generic_visit(node),\n ],\n keywords=[],\n )" ], + [ + "STORE_FAST", + "after_marker" + ], [ "LOAD_GLOBAL", "ast" @@ -2635,6 +3271,10 @@ "CALL_METHOD", "self._create_simple_marker_call(\n super(_NodeVisitor, self).generic_visit(node),\n TreeTracerBase._treetrace_hidden_with_stmt)" ], + [ + "STORE_FAST", + "context_expr" + ], [ "LOAD_GLOBAL", "PY3" @@ -2671,6 +3311,10 @@ "CALL_FUNCTION_KW", "ast.With(\n items=[ast.withitem(context_expr=context_expr)],\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2691,6 +3335,10 @@ "CALL_FUNCTION_KW", "ast.With(\n context_expr=context_expr,\n body=[node],\n )" ], + [ + "STORE_FAST", + "wrapped" + ], [ "LOAD_GLOBAL", "ast" @@ -2795,6 +3443,34 @@ "CALL_FUNCTION_KW", "ast.Call(\n func=ast.Name(id=func.__name__,\n ctx=ast.Load()),\n args=[ast.Num(node._tree_index)],\n keywords=[],\n )" ], + [ + "LOAD_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "class _StmtContext(object):\n __slots__ = ('tracer', 'node', 'frame')\n\n def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame\n\n def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], + [ + "STORE_NAME", + "__slots__" + ], + [ + "STORE_NAME", + " def __init__(self, tracer, node, frame):\n # type: (TreeTracerBase, ast.stmt, FrameType) -> None\n self.tracer = tracer\n self.node = node\n self.frame = frame" + ], + [ + "STORE_NAME", + " def __enter__(self):\n tracer = self.tracer\n node = self.node\n frame = self.frame\n if getattr(node, '_enter_call_node', False):\n tracer._enter_call(node, frame)\n frame_info = tracer.stack[frame]\n frame_info.expression_stack = []\n frame_info.statement_stack.append(node)\n tracer.before_stmt(node, frame)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_val, exc_tb):\n # type: (Type[Exception], Exception, TracebackType) -> bool\n node = self.node\n tracer = self.tracer\n frame = self.frame\n frame_info = tracer.stack[frame]\n\n frame_info.statement_stack.pop()\n\n exc_node = None # type: Optional[Union[ast.expr, ast.stmt]]\n if exc_val and exc_val is not frame_info.exc_value:\n exc_node = node\n frame_info.exc_value = exc_val\n\n # Call the after_expr hook if the exception was raised by an expression\n expression_stack = frame_info.expression_stack\n if expression_stack:\n exc_node = expression_stack[-1]\n tracer._after_expr(exc_node, frame, None, exc_val, exc_tb)\n\n result = tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)\n\n if isinstance(node, ast.Return):\n frame_info.return_node = node\n\n parent = node.parent # type: ast.AST\n return_node = frame_info.return_node\n exiting = (isinstance(parent, (ast.FunctionDef, ast.Module)) and\n (node is parent.body[-1] or\n exc_val or\n return_node))\n if exiting:\n caller_frame, call_node = tracer._get_caller_stuff(frame)\n return_value = None\n if return_node and return_node.value and not exc_val:\n return_value = frame_info.expression_values[return_node.value]\n tracer.exit_call(ExitCallInfo(call_node,\n return_node,\n caller_frame,\n frame,\n return_value,\n exc_val,\n exc_tb\n ))\n\n del tracer.stack[frame]\n for secondary_frame in self.tracer.main_to_secondary_frames.pop(frame):\n del self.tracer.secondary_to_main_frames[secondary_frame]\n\n return result" + ], [ "LOAD_FAST", "tracer" @@ -2839,6 +3515,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2847,6 +3527,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2855,6 +3539,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_GLOBAL", "getattr" @@ -2903,6 +3591,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -2959,6 +3651,10 @@ "LOAD_ATTR", "self.node" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_FAST", "self" @@ -2967,6 +3663,10 @@ "LOAD_ATTR", "self.tracer" ], + [ + "STORE_FAST", + "tracer" + ], [ "LOAD_FAST", "self" @@ -2975,6 +3675,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "tracer" @@ -2991,6 +3695,10 @@ "BINARY_SUBSCR", "tracer.stack[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "frame_info" @@ -3007,6 +3715,10 @@ "CALL_METHOD", "frame_info.statement_stack.pop()" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3031,6 +3743,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "exc_val" @@ -3051,6 +3767,10 @@ "LOAD_ATTR", "frame_info.expression_stack" ], + [ + "STORE_FAST", + "expression_stack" + ], [ "LOAD_FAST", "expression_stack" @@ -3063,6 +3783,10 @@ "BINARY_SUBSCR", "expression_stack[-1]" ], + [ + "STORE_FAST", + "exc_node" + ], [ "LOAD_FAST", "tracer" @@ -3123,6 +3847,10 @@ "CALL_METHOD", "tracer.after_stmt(node, frame, exc_val, exc_tb, exc_node)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3163,6 +3891,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_FAST", "frame_info" @@ -3171,6 +3903,10 @@ "LOAD_ATTR", "frame_info.return_node" ], + [ + "STORE_FAST", + "return_node" + ], [ "LOAD_GLOBAL", "isinstance" @@ -3227,6 +3963,10 @@ "LOAD_FAST", "return_node" ], + [ + "STORE_FAST", + "exiting" + ], [ "LOAD_FAST", "exiting" @@ -3247,6 +3987,18 @@ "CALL_METHOD", "tracer._get_caller_stuff(frame)" ], + [ + "STORE_FAST", + "caller_frame" + ], + [ + "STORE_FAST", + "call_node" + ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "return_node" @@ -3283,6 +4035,10 @@ "BINARY_SUBSCR", "frame_info.expression_values[return_node.value]" ], + [ + "STORE_FAST", + "return_value" + ], [ "LOAD_FAST", "tracer" @@ -3343,6 +4099,10 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "tracer.stack[frame]" + ], [ "LOAD_FAST", "self" @@ -3367,6 +4127,10 @@ "CALL_METHOD", "self.tracer.main_to_secondary_frames.pop(frame)" ], + [ + "STORE_FAST", + "secondary_frame" + ], [ "LOAD_FAST", "self" @@ -3383,6 +4147,10 @@ "LOAD_FAST", "secondary_frame" ], + [ + "DELETE_SUBSCR", + "self.tracer.secondary_to_main_frames[secondary_frame]" + ], [ "LOAD_FAST", "result" @@ -3395,6 +4163,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "node" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3403,6 +4175,10 @@ "LOAD_FAST", "node" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_DEREF", "node" @@ -3411,6 +4187,10 @@ "LOAD_ATTR", "node.parent" ], + [ + "STORE_FAST", + "parent" + ], [ "LOAD_GLOBAL", "AttributeError" @@ -3543,6 +4323,10 @@ "CONTAINS_OP", "node in parent.ifs" ], + [ + "STORE_FAST", + "is_containing_loop" + ], [ "LOAD_FAST", "is_containing_loop" @@ -3615,6 +4399,10 @@ "LOAD_ATTR", "parent.generators" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_DEREF", "node" @@ -3647,6 +4435,10 @@ "CALL_FUNCTION", "list(takewhile(lambda n: n != node, generators))" ], + [ + "STORE_FAST", + "generators" + ], [ "LOAD_FAST", "result" @@ -3675,6 +4467,10 @@ "LOAD_FAST", "parent" ], + [ + "STORE_DEREF", + "node" + ], [ "LOAD_FAST", "result" diff --git a/tests/sample_results/tracer2-py-3.10.json b/tests/sample_results/tracer2-py-3.10.json index ca0a183..4d8f549 100644 --- a/tests/sample_results/tracer2-py-3.10.json +++ b/tests/sample_results/tracer2-py-3.10.json @@ -1,4 +1,96 @@ [ + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import re" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], [ "LOAD_NAME", "find_repr_function" @@ -83,6 +175,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_NAME", "threading" @@ -95,6 +195,10 @@ "CALL_METHOD", "threading.local()" ], + [ + "STORE_NAME", + "thread_global" + ], [ "LOAD_NAME", "os" @@ -119,10 +223,22 @@ "CALL_METHOD", "os.path.dirname((lambda: 0).__code__.co_filename)" ], + [ + "STORE_NAME", + "internal_directories" + ], + [ + "STORE_NAME", + "import birdseye" + ], [ "LOAD_NAME", "ImportError" ], + [ + "LOAD_NAME", + "internal_directories" + ], [ "LOAD_NAME", "os" @@ -147,10 +263,22 @@ "CALL_METHOD", "os.path.dirname(birdseye.__file__)" ], + [ + "STORE_NAME", + "internal_directories" + ], [ "LOAD_NAME", "type" ], + [ + "CALL_FUNCTION", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_NAME", "six" @@ -171,14 +299,54 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "CALL_FUNCTION", "six.add_metaclass(TracerMeta)" ], + [ + "STORE_NAME", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + " def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''" + ], + [ + "STORE_NAME", + " def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables" + ], + [ + "STORE_NAME", + " def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_FAST", "frame" @@ -395,6 +563,10 @@ "LOAD_ATTR", "self.local_reprs" ], + [ + "STORE_FAST", + "old_local_reprs" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -419,6 +591,10 @@ "CALL_METHOD", "self.get_local_reprs(watch, watch_extras)" ], + [ + "CALL_FUNCTION", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], [ "CALL_FUNCTION", "OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" @@ -455,6 +631,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "self" @@ -475,6 +659,10 @@ "CALL_METHOD", "self.comprehension_variables.setdefault(name, [])" ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "values" @@ -555,6 +743,14 @@ "CALL_METHOD", "self.comprehension_variables.items()" ], + [ + "CALL_FUNCTION", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "variables" + ], [ "LOAD_FAST", "self" @@ -571,6 +767,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "name" @@ -627,6 +831,18 @@ "LOAD_FAST", "variables" ], + [ + "LOAD_FAST", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -643,6 +859,18 @@ "CALL_FUNCTION", "my_cheap_repr(value)" ], + [ + "LOAD_FAST", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "name" @@ -667,6 +895,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -675,6 +907,10 @@ "LOAD_ATTR", "frame.f_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_FAST", "code" @@ -735,6 +971,10 @@ "BINARY_ADD", "code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())" ], + [ + "STORE_DEREF", + "vars_order" + ], [ "LOAD_GLOBAL", "sorted" @@ -759,10 +999,22 @@ "CALL_FUNCTION_KW", "sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )" ], + [ + "STORE_FAST", + "result_items" + ], [ "LOAD_FAST", "watch" ], + [ + "STORE_FAST", + "variable" + ], + [ + "LOAD_FAST", + "result_items" + ], [ "LOAD_GLOBAL", "sorted" @@ -787,10 +1039,22 @@ "CALL_FUNCTION", "sorted(variable.items(frame))" ], + [ + "STORE_FAST", + "result_items" + ], [ "LOAD_FAST", "result_items" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -803,6 +1067,10 @@ "LOAD_FAST", "watch_extras" ], + [ + "STORE_FAST", + "extra" + ], [ "LOAD_FAST", "extra" @@ -819,6 +1087,10 @@ "CALL_FUNCTION", "extra(source, value)" ], + [ + "STORE_FAST", + "pair" + ], [ "LOAD_GLOBAL", "Exception" @@ -871,6 +1143,34 @@ "CALL_METHOD", "vars_order.index(key_value[0])" ], + [ + "LOAD_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + " def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result" + ], + [ + "STORE_NAME", + " def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)" + ], + [ + "STORE_NAME", + " def __enter__(self):\n return self.default.__enter__(context=1)" + ], + [ + "STORE_NAME", + " def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_GLOBAL", "super" @@ -907,6 +1207,10 @@ "CALL_FUNCTION_EX", "super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1031,6 +1335,34 @@ "CALL_FUNCTION_EX", "self.default.__exit__(*args, context=1)" ], + [ + "STORE_NAME", + " def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()" + ], + [ + "STORE_NAME", + " def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper" + ], + [ + "STORE_NAME", + " def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)" + ], + [ + "STORE_NAME", + " def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)" + ], + [ + "STORE_NAME", + " def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames" + ], + [ + "STORE_NAME", + " def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1043,6 +1375,10 @@ "CALL_FUNCTION", "ensure_tuple(watch)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1055,6 +1391,10 @@ "CALL_FUNCTION", "ensure_tuple(watch_explode)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], [ "BINARY_ADD", "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" @@ -1143,6 +1483,14 @@ "STORE_ATTR", "self.target_frames" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1175,6 +1523,14 @@ "CALL_FUNCTION", "CommonVariable(v)" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1271,6 +1627,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)" + ], [ "LOAD_GLOBAL", "functools" @@ -1291,6 +1651,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e" + ], [ "LOAD_GLOBAL", "inspect" @@ -1335,6 +1699,10 @@ "CALL_FUNCTION_EX", "function(*args, **kwargs)" ], + [ + "CALL_FUNCTION", + " with self:\n return function(*args, **kwargs)" + ], [ "LOAD_DEREF", "function" @@ -1351,6 +1719,10 @@ "CALL_FUNCTION_EX", "function(*args, **kwargs)" ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "gen" @@ -1359,6 +1731,14 @@ "LOAD_ATTR", "gen.send" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_DEREF", "self" @@ -1375,10 +1755,22 @@ "CALL_FUNCTION", "method(incoming)" ], + [ + "STORE_FAST", + "outgoing" + ], [ "LOAD_GLOBAL", "StopIteration" ], + [ + "CALL_FUNCTION", + " with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return" + ], + [ + "CALL_FUNCTION", + " with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return" + ], [ "LOAD_FAST", "gen" @@ -1391,6 +1783,14 @@ "LOAD_FAST", "outgoing" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_GLOBAL", "Exception" @@ -1407,6 +1807,14 @@ "LOAD_FAST", "e" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_FAST", "self" @@ -1439,6 +1847,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1539,6 +1951,10 @@ "CALL_METHOD", "thread_global.__dict__.setdefault('original_trace_functions', [])" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_FAST", "stack" @@ -1603,6 +2019,10 @@ "LOAD_ATTR", "thread_global.original_trace_functions" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_GLOBAL", "sys" @@ -1647,6 +2067,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1823,6 +2247,14 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "is_comprehension_frame" @@ -1843,6 +2275,18 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "self" @@ -1867,6 +2311,10 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], [ "LOAD_FAST", "i" @@ -1919,6 +2367,10 @@ "LOAD_ATTR", "self.config.thread_local" ], + [ + "STORE_FAST", + "thread_local" + ], [ "LOAD_FAST", "thread_local" @@ -1951,6 +2403,10 @@ "BINARY_SUBSCR", "self.frame_infos[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "event" @@ -1963,6 +2419,10 @@ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2007,6 +2467,10 @@ "LOAD_ATTR", "frame_info.last_line_no" ], + [ + "STORE_FAST", + "line_no" + ], [ "LOAD_GLOBAL", "Event" @@ -2039,6 +2503,10 @@ "CALL_FUNCTION_KW", "Event(frame_info, event, arg, thread_local.depth, line_no=line_no)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "self" @@ -2063,6 +2531,10 @@ "CALL_METHOD", "self.config.formatter.format_line_only(trace_event)" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "self" @@ -2143,6 +2615,10 @@ "CALL_FUNCTION", "Event(frame_info, event, arg, thread_local.depth)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "frame" @@ -2231,10 +2707,18 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "self.frame_infos[frame]" + ], [ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2263,6 +2747,10 @@ "CALL_METHOD", "self.config.formatter.format(trace_event)" ], + [ + "STORE_FAST", + "formatted" + ], [ "LOAD_FAST", "self" @@ -2291,6 +2779,30 @@ "LOAD_ATTR", "self.trace" ], + [ + "LOAD_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + " def __init__(self, config):\n self.config = config" + ], + [ + "STORE_NAME", + " def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator" + ], + [ + "STORE_NAME", + " def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "config" @@ -2315,6 +2827,10 @@ "CALL_FUNCTION", "Exception(\"birdseye doesn't support this version of Python\")" ], + [ + "STORE_FAST", + "import birdseye" + ], [ "LOAD_GLOBAL", "ImportError" @@ -2363,6 +2879,10 @@ "CALL_METHOD", "self._trace(args[0])" ], + [ + "STORE_FAST", + " def decorator(func):\n return self._trace(func, *args, **kwargs)" + ], [ "LOAD_FAST", "decorator" @@ -2391,6 +2911,10 @@ "CALL_FUNCTION_EX", "self._trace(func, *args, **kwargs)" ], + [ + "STORE_FAST", + "from birdseye import eye" + ], [ "LOAD_FAST", "eye" @@ -2403,6 +2927,10 @@ "CALL_FUNCTION", "eye(func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +2963,10 @@ "CALL_FUNCTION", "self.config.snoop(*args, **kwargs)(traced)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "functools" @@ -2455,6 +2987,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -2475,10 +3011,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_DEREF", "func" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_FAST", "final_func" diff --git a/tests/sample_results/tracer2-py-3.8.json b/tests/sample_results/tracer2-py-3.8.json index 680c52a..ea17074 100644 --- a/tests/sample_results/tracer2-py-3.8.json +++ b/tests/sample_results/tracer2-py-3.8.json @@ -1,4 +1,96 @@ [ + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import re" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], [ "LOAD_NAME", "find_repr_function" @@ -83,6 +175,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_NAME", "threading" @@ -95,6 +195,10 @@ "CALL_METHOD", "threading.local()" ], + [ + "STORE_NAME", + "thread_global" + ], [ "LOAD_NAME", "os" @@ -119,10 +223,22 @@ "CALL_METHOD", "os.path.dirname((lambda: 0).__code__.co_filename)" ], + [ + "STORE_NAME", + "internal_directories" + ], + [ + "STORE_NAME", + "import birdseye" + ], [ "LOAD_NAME", "ImportError" ], + [ + "LOAD_NAME", + "internal_directories += (os.path.dirname(birdseye.__file__),)" + ], [ "LOAD_NAME", "os" @@ -147,10 +263,22 @@ "CALL_METHOD", "os.path.dirname(birdseye.__file__)" ], + [ + "STORE_NAME", + "internal_directories += (os.path.dirname(birdseye.__file__),)" + ], [ "LOAD_NAME", "type" ], + [ + "CALL_FUNCTION", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_NAME", "six" @@ -171,14 +299,54 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "CALL_FUNCTION", "six.add_metaclass(TracerMeta)" ], + [ + "STORE_NAME", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + " def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''" + ], + [ + "STORE_NAME", + " def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables" + ], + [ + "STORE_NAME", + " def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_FAST", "frame" @@ -395,6 +563,10 @@ "LOAD_ATTR", "self.local_reprs" ], + [ + "STORE_FAST", + "old_local_reprs" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -419,6 +591,10 @@ "CALL_METHOD", "self.get_local_reprs(watch, watch_extras)" ], + [ + "CALL_FUNCTION", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], [ "CALL_FUNCTION", "OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" @@ -455,6 +631,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "self" @@ -475,6 +659,10 @@ "CALL_METHOD", "self.comprehension_variables.setdefault(name, [])" ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "values" @@ -555,6 +743,14 @@ "CALL_METHOD", "self.comprehension_variables.items()" ], + [ + "CALL_FUNCTION", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "variables" + ], [ "LOAD_FAST", "self" @@ -571,6 +767,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "name" @@ -627,6 +831,18 @@ "LOAD_FAST", "variables" ], + [ + "LOAD_FAST", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -643,6 +859,18 @@ "CALL_FUNCTION", "my_cheap_repr(value)" ], + [ + "LOAD_FAST", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "name" @@ -667,6 +895,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -675,6 +907,10 @@ "LOAD_ATTR", "frame.f_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_FAST", "code" @@ -735,6 +971,10 @@ "BINARY_ADD", "code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())" ], + [ + "STORE_DEREF", + "vars_order" + ], [ "LOAD_GLOBAL", "sorted" @@ -759,10 +999,22 @@ "CALL_FUNCTION_KW", "sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )" ], + [ + "STORE_FAST", + "result_items" + ], [ "LOAD_FAST", "watch" ], + [ + "STORE_FAST", + "variable" + ], + [ + "LOAD_FAST", + "result_items += sorted(variable.items(frame))" + ], [ "LOAD_GLOBAL", "sorted" @@ -787,10 +1039,22 @@ "CALL_FUNCTION", "sorted(variable.items(frame))" ], + [ + "STORE_FAST", + "result_items += sorted(variable.items(frame))" + ], [ "LOAD_FAST", "result_items" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -803,6 +1067,10 @@ "LOAD_FAST", "watch_extras" ], + [ + "STORE_FAST", + "extra" + ], [ "LOAD_FAST", "extra" @@ -819,6 +1087,10 @@ "CALL_FUNCTION", "extra(source, value)" ], + [ + "STORE_FAST", + "pair" + ], [ "LOAD_GLOBAL", "Exception" @@ -871,6 +1143,34 @@ "CALL_METHOD", "vars_order.index(key_value[0])" ], + [ + "LOAD_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + " def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result" + ], + [ + "STORE_NAME", + " def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)" + ], + [ + "STORE_NAME", + " def __enter__(self):\n return self.default.__enter__(context=1)" + ], + [ + "STORE_NAME", + " def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_GLOBAL", "super" @@ -907,6 +1207,10 @@ "CALL_FUNCTION_EX", "super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1031,6 +1335,34 @@ "CALL_FUNCTION_EX", "self.default.__exit__(*args, context=1)" ], + [ + "STORE_NAME", + " def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()" + ], + [ + "STORE_NAME", + " def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper" + ], + [ + "STORE_NAME", + " def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)" + ], + [ + "STORE_NAME", + " def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)" + ], + [ + "STORE_NAME", + " def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames" + ], + [ + "STORE_NAME", + " def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1043,6 +1375,10 @@ "CALL_FUNCTION", "ensure_tuple(watch)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1055,6 +1391,10 @@ "CALL_FUNCTION", "ensure_tuple(watch_explode)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], [ "BINARY_ADD", "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" @@ -1143,6 +1483,14 @@ "STORE_ATTR", "self.target_frames" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1175,6 +1523,14 @@ "CALL_FUNCTION", "CommonVariable(v)" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1271,6 +1627,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)" + ], [ "LOAD_GLOBAL", "functools" @@ -1291,6 +1651,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e" + ], [ "LOAD_GLOBAL", "inspect" @@ -1351,6 +1715,10 @@ "CALL_FUNCTION_EX", "function(*args, **kwargs)" ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "gen" @@ -1359,6 +1727,14 @@ "LOAD_ATTR", "gen.send" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_DEREF", "self" @@ -1375,6 +1751,10 @@ "CALL_FUNCTION", "method(incoming)" ], + [ + "STORE_FAST", + "outgoing" + ], [ "LOAD_GLOBAL", "StopIteration" @@ -1391,6 +1771,14 @@ "LOAD_FAST", "outgoing" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_GLOBAL", "Exception" @@ -1407,6 +1795,14 @@ "LOAD_FAST", "e" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_FAST", "self" @@ -1439,6 +1835,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1539,6 +1939,10 @@ "CALL_METHOD", "thread_global.__dict__.setdefault('original_trace_functions', [])" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_FAST", "stack" @@ -1603,6 +2007,10 @@ "LOAD_ATTR", "thread_global.original_trace_functions" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_GLOBAL", "sys" @@ -1647,6 +2055,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1823,6 +2235,14 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "is_comprehension_frame" @@ -1843,6 +2263,18 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "LOAD_FAST", + "i += 1" + ], + [ + "STORE_FAST", + "i += 1" + ], [ "LOAD_FAST", "self" @@ -1867,6 +2299,10 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], [ "LOAD_FAST", "i" @@ -1919,6 +2355,10 @@ "LOAD_ATTR", "self.config.thread_local" ], + [ + "STORE_FAST", + "thread_local" + ], [ "LOAD_FAST", "thread_local" @@ -1951,6 +2391,10 @@ "BINARY_SUBSCR", "self.frame_infos[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "event" @@ -1963,6 +2407,10 @@ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2007,6 +2455,10 @@ "LOAD_ATTR", "frame_info.last_line_no" ], + [ + "STORE_FAST", + "line_no" + ], [ "LOAD_GLOBAL", "Event" @@ -2039,6 +2491,10 @@ "CALL_FUNCTION_KW", "Event(frame_info, event, arg, thread_local.depth, line_no=line_no)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "self" @@ -2063,6 +2519,10 @@ "CALL_METHOD", "self.config.formatter.format_line_only(trace_event)" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "self" @@ -2143,6 +2603,10 @@ "CALL_FUNCTION", "Event(frame_info, event, arg, thread_local.depth)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "frame" @@ -2231,10 +2695,18 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "self.frame_infos[frame]" + ], [ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2263,6 +2735,10 @@ "CALL_METHOD", "self.config.formatter.format(trace_event)" ], + [ + "STORE_FAST", + "formatted" + ], [ "LOAD_FAST", "self" @@ -2291,6 +2767,30 @@ "LOAD_ATTR", "self.trace" ], + [ + "LOAD_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + " def __init__(self, config):\n self.config = config" + ], + [ + "STORE_NAME", + " def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator" + ], + [ + "STORE_NAME", + " def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "config" @@ -2315,6 +2815,10 @@ "CALL_FUNCTION", "Exception(\"birdseye doesn't support this version of Python\")" ], + [ + "STORE_FAST", + "import birdseye" + ], [ "LOAD_GLOBAL", "ImportError" @@ -2363,6 +2867,10 @@ "CALL_METHOD", "self._trace(args[0])" ], + [ + "STORE_FAST", + " def decorator(func):\n return self._trace(func, *args, **kwargs)" + ], [ "LOAD_FAST", "decorator" @@ -2391,6 +2899,10 @@ "CALL_FUNCTION_EX", "self._trace(func, *args, **kwargs)" ], + [ + "STORE_FAST", + "from birdseye import eye" + ], [ "LOAD_FAST", "eye" @@ -2403,6 +2915,10 @@ "CALL_FUNCTION", "eye(func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +2951,10 @@ "CALL_FUNCTION", "self.config.snoop(*args, **kwargs)(traced)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "functools" @@ -2455,6 +2975,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -2475,10 +2999,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_DEREF", "func" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_FAST", "final_func" diff --git a/tests/sample_results/tracer2-py-3.9.json b/tests/sample_results/tracer2-py-3.9.json index ca0a183..cf1163e 100644 --- a/tests/sample_results/tracer2-py-3.9.json +++ b/tests/sample_results/tracer2-py-3.9.json @@ -1,4 +1,96 @@ [ + [ + "STORE_NAME", + "import functools" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import re" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "import threading" + ], + [ + "STORE_NAME", + "from collections import OrderedDict" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, find_repr_function" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \\\n truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .formatting import Event, Source" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], + [ + "STORE_NAME", + "from .variables import CommonVariable, Exploding, BaseVariable" + ], [ "LOAD_NAME", "find_repr_function" @@ -83,6 +175,14 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_NAME", "threading" @@ -95,6 +195,10 @@ "CALL_METHOD", "threading.local()" ], + [ + "STORE_NAME", + "thread_global" + ], [ "LOAD_NAME", "os" @@ -119,10 +223,22 @@ "CALL_METHOD", "os.path.dirname((lambda: 0).__code__.co_filename)" ], + [ + "STORE_NAME", + "internal_directories" + ], + [ + "STORE_NAME", + "import birdseye" + ], [ "LOAD_NAME", "ImportError" ], + [ + "LOAD_NAME", + "internal_directories" + ], [ "LOAD_NAME", "os" @@ -147,10 +263,22 @@ "CALL_METHOD", "os.path.dirname(birdseye.__file__)" ], + [ + "STORE_NAME", + "internal_directories" + ], [ "LOAD_NAME", "type" ], + [ + "CALL_FUNCTION", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_NAME", "six" @@ -171,14 +299,54 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "CALL_FUNCTION", "six.add_metaclass(TracerMeta)" ], + [ + "STORE_NAME", + "@six.add_metaclass(TracerMeta)\nclass Tracer(object):\n def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()\n\n def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper\n\n def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)\n\n def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)\n\n def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)\n \n def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames\n\n def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "LOAD_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + "class FrameInfo(object):\n def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''\n\n def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables\n\n def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], + [ + "STORE_NAME", + " def __init__(self, frame):\n self.frame = frame\n self.local_reprs = {}\n self.last_line_no = frame.f_lineno\n self.comprehension_variables = OrderedDict()\n self.source = Source.for_frame(frame)\n self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR\n self.had_exception = False\n if is_comprehension_frame(frame):\n self.comprehension_type = (\n re.match(r'<(\\w+)comp>', frame.f_code.co_name).group(1).title()\n + u' comprehension'\n )\n else:\n self.comprehension_type = ''" + ], + [ + "STORE_NAME", + " def update_variables(self, watch, watch_extras, event):\n self.last_line_no = self.frame.f_lineno\n old_local_reprs = self.local_reprs\n self.local_reprs = OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )\n\n if self.comprehension_type:\n for name, value_repr in self.local_reprs.items():\n values = self.comprehension_variables.setdefault(name, [])\n if not values or values[-1] != value_repr:\n values.append(value_repr)\n values[:] = truncate_list(values, 11)\n if event in ('return', 'exception'):\n return [\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]\n else:\n return []\n\n variables = []\n for name, value_repr in self.local_reprs.items():\n if name not in old_local_reprs or old_local_reprs[name] != value_repr:\n variables.append((name, value_repr))\n return variables" + ], + [ + "STORE_NAME", + " def get_local_reprs(self, watch, watch_extras):\n frame = self.frame\n code = frame.f_code\n vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())\n\n result_items = sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )\n\n for variable in watch:\n result_items += sorted(variable.items(frame))\n\n for source, value in result_items:\n yield source, value\n for extra in watch_extras:\n try:\n pair = extra(source, value)\n except Exception:\n pass\n else:\n if pair is not None:\n assert len(pair) == 2, \"Watch extra must return pair or None\"\n yield pair" + ], [ "LOAD_FAST", "frame" @@ -395,6 +563,10 @@ "LOAD_ATTR", "self.local_reprs" ], + [ + "STORE_FAST", + "old_local_reprs" + ], [ "LOAD_GLOBAL", "OrderedDict" @@ -419,6 +591,10 @@ "CALL_METHOD", "self.get_local_reprs(watch, watch_extras)" ], + [ + "CALL_FUNCTION", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], [ "CALL_FUNCTION", "OrderedDict(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" @@ -455,6 +631,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "self" @@ -475,6 +659,10 @@ "CALL_METHOD", "self.comprehension_variables.setdefault(name, [])" ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "values" @@ -555,6 +743,14 @@ "CALL_METHOD", "self.comprehension_variables.items()" ], + [ + "CALL_FUNCTION", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "variables" + ], [ "LOAD_FAST", "self" @@ -571,6 +767,14 @@ "CALL_METHOD", "self.local_reprs.items()" ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "value_repr" + ], [ "LOAD_FAST", "name" @@ -627,6 +831,18 @@ "LOAD_FAST", "variables" ], + [ + "LOAD_FAST", + "(\n (source, my_cheap_repr(value))\n for source, value in\n self.get_local_reprs(watch, watch_extras)\n )" + ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -643,6 +859,18 @@ "CALL_FUNCTION", "my_cheap_repr(value)" ], + [ + "LOAD_FAST", + "[\n (name, ', '.join(values))\n for name, values in self.comprehension_variables.items()\n ]" + ], + [ + "STORE_FAST", + "name" + ], + [ + "STORE_FAST", + "values" + ], [ "LOAD_FAST", "name" @@ -667,6 +895,10 @@ "LOAD_ATTR", "self.frame" ], + [ + "STORE_FAST", + "frame" + ], [ "LOAD_FAST", "frame" @@ -675,6 +907,10 @@ "LOAD_ATTR", "frame.f_code" ], + [ + "STORE_FAST", + "code" + ], [ "LOAD_FAST", "code" @@ -735,6 +971,10 @@ "BINARY_ADD", "code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys())" ], + [ + "STORE_DEREF", + "vars_order" + ], [ "LOAD_GLOBAL", "sorted" @@ -759,10 +999,22 @@ "CALL_FUNCTION_KW", "sorted(\n frame.f_locals.items(),\n key=lambda key_value: vars_order.index(key_value[0])\n )" ], + [ + "STORE_FAST", + "result_items" + ], [ "LOAD_FAST", "watch" ], + [ + "STORE_FAST", + "variable" + ], + [ + "LOAD_FAST", + "result_items" + ], [ "LOAD_GLOBAL", "sorted" @@ -787,10 +1039,22 @@ "CALL_FUNCTION", "sorted(variable.items(frame))" ], + [ + "STORE_FAST", + "result_items" + ], [ "LOAD_FAST", "result_items" ], + [ + "STORE_FAST", + "source" + ], + [ + "STORE_FAST", + "value" + ], [ "LOAD_FAST", "source" @@ -803,6 +1067,10 @@ "LOAD_FAST", "watch_extras" ], + [ + "STORE_FAST", + "extra" + ], [ "LOAD_FAST", "extra" @@ -819,6 +1087,10 @@ "CALL_FUNCTION", "extra(source, value)" ], + [ + "STORE_FAST", + "pair" + ], [ "LOAD_GLOBAL", "Exception" @@ -871,6 +1143,34 @@ "CALL_METHOD", "vars_order.index(key_value[0])" ], + [ + "LOAD_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + "class TracerMeta(type):\n def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result\n\n def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)\n\n def __enter__(self):\n return self.default.__enter__(context=1)\n\n def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], + [ + "STORE_NAME", + " def __new__(mcs, *args, **kwargs):\n result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)\n result.default = result()\n return result" + ], + [ + "STORE_NAME", + " def __call__(cls, *args, **kwargs):\n if no_args_decorator(args, kwargs):\n return cls.default(args[0])\n else:\n return super(TracerMeta, cls).__call__(*args, **kwargs)" + ], + [ + "STORE_NAME", + " def __enter__(self):\n return self.default.__enter__(context=1)" + ], + [ + "STORE_NAME", + " def __exit__(self, *args):\n return self.default.__exit__(*args, context=1)" + ], [ "LOAD_GLOBAL", "super" @@ -907,6 +1207,10 @@ "CALL_FUNCTION_EX", "super(TracerMeta, mcs).__new__(mcs, *args, **kwargs)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -1031,6 +1335,34 @@ "CALL_FUNCTION_EX", "self.default.__exit__(*args, context=1)" ], + [ + "STORE_NAME", + " def __init__(\n self,\n watch=(),\n watch_explode=(),\n depth=1,\n ):\n self.watch = [\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]\n self.frame_infos = ArgDefaultDict(FrameInfo)\n self.depth = depth\n assert self.depth >= 1\n self.target_codes = set()\n self.target_frames = set()" + ], + [ + "STORE_NAME", + " def __call__(self, function):\n if iscoroutinefunction(function):\n raise NotImplementedError(\"coroutines are not supported, sorry!\")\n\n self.target_codes.add(function.__code__)\n\n @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)\n\n @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e\n\n if inspect.isgeneratorfunction(function):\n return generator_wrapper\n else:\n return simple_wrapper" + ], + [ + "STORE_NAME", + " def __enter__(self, context=0):\n if not self.config.enabled:\n return\n\n calling_frame = sys._getframe(context + 1)\n if not self._is_internal_frame(calling_frame):\n calling_frame.f_trace = self.trace\n self.target_frames.add(calling_frame)\n self.config.last_frame = calling_frame\n self.trace(calling_frame, 'enter', None)\n\n stack = thread_global.__dict__.setdefault('original_trace_functions', [])\n stack.append(sys.gettrace())\n sys.settrace(self.trace)" + ], + [ + "STORE_NAME", + " def __exit__(self, exc_type, exc_value, exc_traceback, context=0):\n if not self.config.enabled:\n return\n\n stack = thread_global.original_trace_functions\n sys.settrace(stack.pop())\n calling_frame = sys._getframe(context + 1)\n self.trace(calling_frame, 'exit', None)\n self.target_frames.discard(calling_frame)\n self.frame_infos.pop(calling_frame, None)" + ], + [ + "STORE_NAME", + " def _is_internal_frame(self, frame):\n return frame.f_code.co_filename.startswith(internal_directories)" + ], + [ + "STORE_NAME", + " def _is_traced_frame(self, frame):\n return frame.f_code in self.target_codes or frame in self.target_frames" + ], + [ + "STORE_NAME", + " def trace(self, frame, event, arg):\n if not self._is_traced_frame(frame):\n if (\n self.depth == 1\n or self._is_internal_frame(frame)\n ) and not is_comprehension_frame(frame):\n return None\n else:\n candidate = frame\n i = 0\n while True:\n if is_comprehension_frame(candidate):\n candidate = candidate.f_back\n continue\n i += 1\n if self._is_traced_frame(candidate):\n break\n candidate = candidate.f_back\n if i >= self.depth or candidate is None or self._is_internal_frame(candidate):\n return None\n\n thread_local = self.config.thread_local\n thread_local.__dict__.setdefault('depth', -1)\n frame_info = self.frame_infos[frame]\n if event in ('call', 'enter'):\n thread_local.depth += 1\n elif self.config.last_frame and self.config.last_frame is not frame:\n line_no = frame_info.last_line_no\n trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no)\n line = self.config.formatter.format_line_only(trace_event)\n self.config.write(line)\n\n if event == 'exception':\n frame_info.had_exception = True\n\n self.config.last_frame = frame\n\n trace_event = Event(frame_info, event, arg, thread_local.depth)\n if not (frame.f_code.co_name == '' and event not in ('return', 'exception')):\n trace_event.variables = frame_info.update_variables(\n self.watch,\n self.config.watch_extras,\n event,\n )\n\n if event in ('return', 'exit'):\n del self.frame_infos[frame]\n thread_local.depth -= 1\n\n formatted = self.config.formatter.format(trace_event)\n self.config.write(formatted)\n\n return self.trace" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1043,6 +1375,10 @@ "CALL_FUNCTION", "ensure_tuple(watch)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], [ "LOAD_GLOBAL", "ensure_tuple" @@ -1055,6 +1391,10 @@ "CALL_FUNCTION", "ensure_tuple(watch_explode)" ], + [ + "CALL_FUNCTION", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], [ "BINARY_ADD", "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ] + [\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" @@ -1143,6 +1483,14 @@ "STORE_ATTR", "self.target_frames" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else CommonVariable(v)\n for v in ensure_tuple(watch)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1175,6 +1523,14 @@ "CALL_FUNCTION", "CommonVariable(v)" ], + [ + "LOAD_FAST", + "[\n v if isinstance(v, BaseVariable) else Exploding(v)\n for v in ensure_tuple(watch_explode)\n ]" + ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_GLOBAL", "isinstance" @@ -1271,6 +1627,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def simple_wrapper(*args, **kwargs):\n with self:\n return function(*args, **kwargs)" + ], [ "LOAD_GLOBAL", "functools" @@ -1291,6 +1651,10 @@ "CALL_FUNCTION", "functools.wraps(function)" ], + [ + "STORE_FAST", + " @functools.wraps(function)\n def generator_wrapper(*args, **kwargs):\n gen = function(*args, **kwargs)\n method, incoming = gen.send, None\n while True:\n with self:\n try:\n outgoing = method(incoming)\n except StopIteration:\n return\n try:\n method, incoming = gen.send, (yield outgoing)\n except Exception as e:\n method, incoming = gen.throw, e" + ], [ "LOAD_GLOBAL", "inspect" @@ -1351,6 +1715,10 @@ "CALL_FUNCTION_EX", "function(*args, **kwargs)" ], + [ + "STORE_FAST", + "gen" + ], [ "LOAD_FAST", "gen" @@ -1359,6 +1727,14 @@ "LOAD_ATTR", "gen.send" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_DEREF", "self" @@ -1375,6 +1751,10 @@ "CALL_FUNCTION", "method(incoming)" ], + [ + "STORE_FAST", + "outgoing" + ], [ "LOAD_GLOBAL", "StopIteration" @@ -1391,6 +1771,14 @@ "LOAD_FAST", "outgoing" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_GLOBAL", "Exception" @@ -1407,6 +1795,14 @@ "LOAD_FAST", "e" ], + [ + "STORE_FAST", + "method" + ], + [ + "STORE_FAST", + "incoming" + ], [ "LOAD_FAST", "self" @@ -1439,6 +1835,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1539,6 +1939,10 @@ "CALL_METHOD", "thread_global.__dict__.setdefault('original_trace_functions', [])" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_FAST", "stack" @@ -1603,6 +2007,10 @@ "LOAD_ATTR", "thread_global.original_trace_functions" ], + [ + "STORE_FAST", + "stack" + ], [ "LOAD_GLOBAL", "sys" @@ -1647,6 +2055,10 @@ "CALL_METHOD", "sys._getframe(context + 1)" ], + [ + "STORE_FAST", + "calling_frame" + ], [ "LOAD_FAST", "self" @@ -1823,6 +2235,14 @@ "LOAD_FAST", "frame" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_GLOBAL", "is_comprehension_frame" @@ -1843,6 +2263,18 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], + [ + "LOAD_FAST", + "i" + ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "self" @@ -1867,6 +2299,10 @@ "LOAD_ATTR", "candidate.f_back" ], + [ + "STORE_FAST", + "candidate" + ], [ "LOAD_FAST", "i" @@ -1919,6 +2355,10 @@ "LOAD_ATTR", "self.config.thread_local" ], + [ + "STORE_FAST", + "thread_local" + ], [ "LOAD_FAST", "thread_local" @@ -1951,6 +2391,10 @@ "BINARY_SUBSCR", "self.frame_infos[frame]" ], + [ + "STORE_FAST", + "frame_info" + ], [ "LOAD_FAST", "event" @@ -1963,6 +2407,10 @@ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2007,6 +2455,10 @@ "LOAD_ATTR", "frame_info.last_line_no" ], + [ + "STORE_FAST", + "line_no" + ], [ "LOAD_GLOBAL", "Event" @@ -2039,6 +2491,10 @@ "CALL_FUNCTION_KW", "Event(frame_info, event, arg, thread_local.depth, line_no=line_no)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "self" @@ -2063,6 +2519,10 @@ "CALL_METHOD", "self.config.formatter.format_line_only(trace_event)" ], + [ + "STORE_FAST", + "line" + ], [ "LOAD_FAST", "self" @@ -2143,6 +2603,10 @@ "CALL_FUNCTION", "Event(frame_info, event, arg, thread_local.depth)" ], + [ + "STORE_FAST", + "trace_event" + ], [ "LOAD_FAST", "frame" @@ -2231,10 +2695,18 @@ "LOAD_FAST", "frame" ], + [ + "DELETE_SUBSCR", + "self.frame_infos[frame]" + ], [ "LOAD_FAST", "thread_local" ], + [ + "LOAD_ATTR", + "thread_local.depth" + ], [ "STORE_ATTR", "thread_local.depth" @@ -2263,6 +2735,10 @@ "CALL_METHOD", "self.config.formatter.format(trace_event)" ], + [ + "STORE_FAST", + "formatted" + ], [ "LOAD_FAST", "self" @@ -2291,6 +2767,30 @@ "LOAD_ATTR", "self.trace" ], + [ + "LOAD_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + "class Spy(object):\n def __init__(self, config):\n self.config = config\n\n def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator\n\n def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], + [ + "STORE_NAME", + " def __init__(self, config):\n self.config = config" + ], + [ + "STORE_NAME", + " def __call__(self, *args, **kwargs):\n if NO_ASTTOKENS:\n raise Exception(\"birdseye doesn't support this version of Python\")\n\n try:\n import birdseye\n except ImportError:\n raise Exception(\"You must install birdseye separately to use spy: pip install birdseye\")\n\n # Decorator without parentheses\n if no_args_decorator(args, kwargs):\n return self._trace(args[0])\n\n # Decorator with parentheses and perhaps arguments\n def decorator(func):\n return self._trace(func, *args, **kwargs)\n\n return decorator" + ], + [ + "STORE_NAME", + " def _trace(self, func, *args, **kwargs):\n # noinspection PyUnresolvedReferences\n from birdseye import eye\n\n traced = eye(func)\n traced = self.config.snoop(*args, **kwargs)(traced)\n\n @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)\n\n return wrapper" + ], [ "LOAD_FAST", "config" @@ -2315,6 +2815,10 @@ "CALL_FUNCTION", "Exception(\"birdseye doesn't support this version of Python\")" ], + [ + "STORE_FAST", + "import birdseye" + ], [ "LOAD_GLOBAL", "ImportError" @@ -2363,6 +2867,10 @@ "CALL_METHOD", "self._trace(args[0])" ], + [ + "STORE_FAST", + " def decorator(func):\n return self._trace(func, *args, **kwargs)" + ], [ "LOAD_FAST", "decorator" @@ -2391,6 +2899,10 @@ "CALL_FUNCTION_EX", "self._trace(func, *args, **kwargs)" ], + [ + "STORE_FAST", + "from birdseye import eye" + ], [ "LOAD_FAST", "eye" @@ -2403,6 +2915,10 @@ "CALL_FUNCTION", "eye(func)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_DEREF", "self" @@ -2435,6 +2951,10 @@ "CALL_FUNCTION", "self.config.snoop(*args, **kwargs)(traced)" ], + [ + "STORE_DEREF", + "traced" + ], [ "LOAD_GLOBAL", "functools" @@ -2455,6 +2975,10 @@ "CALL_FUNCTION", "functools.wraps(func)" ], + [ + "STORE_FAST", + " @functools.wraps(func)\n def wrapper(*func_args, **func_kwargs):\n if self.config.enabled:\n final_func = traced\n else:\n final_func = func\n\n return final_func(*func_args, **func_kwargs)" + ], [ "LOAD_FAST", "wrapper" @@ -2475,10 +2999,18 @@ "LOAD_DEREF", "traced" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_DEREF", "func" ], + [ + "STORE_FAST", + "final_func" + ], [ "LOAD_FAST", "final_func" diff --git a/tests/sample_results/utils-py-3.10.json b/tests/sample_results/utils-py-3.10.json index d5799db..7e33470 100644 --- a/tests/sample_results/utils-py-3.10.json +++ b/tests/sample_results/utils-py-3.10.json @@ -1,4 +1,28 @@ [ + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "from __future__ import print_function, division, absolute_import" + ], + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import json" + ], + [ + "STORE_NAME", + "from future import standard_library" + ], [ "LOAD_NAME", "standard_library" @@ -11,6 +35,62 @@ "CALL_METHOD", "standard_library.install_aliases()" ], + [ + "STORE_NAME", + "import token" + ], + [ + "STORE_NAME", + "from future.utils import raise_from" + ], + [ + "STORE_NAME", + "import ntpath" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import types" + ], + [ + "STORE_NAME", + "from sys import version_info" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable" + ], + [ + "STORE_NAME", + "from typing import Type" + ], [ "LOAD_NAME", "ImportError" @@ -19,14 +99,38 @@ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "Type" + ], + [ + "STORE_NAME", + "from typing import Deque" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from collections import deque as Deque" + ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from backports.functools_lru_cache import lru_cache" + ], + [ + "STORE_NAME", + "from littleutils import strip_required_prefix" + ], [ "LOAD_NAME", "version_info" @@ -39,6 +143,10 @@ "COMPARE_OP", "version_info.major == 2" ], + [ + "STORE_NAME", + "PY2" + ], [ "LOAD_NAME", "PY2" @@ -47,6 +155,10 @@ "UNARY_NOT", "not PY2" ], + [ + "STORE_NAME", + "PY3" + ], [ "LOAD_NAME", "TypeVar" @@ -55,6 +167,10 @@ "CALL_FUNCTION", "TypeVar('T')" ], + [ + "STORE_NAME", + "T" + ], [ "LOAD_NAME", "TypeVar" @@ -63,6 +179,18 @@ "CALL_FUNCTION", "TypeVar('RT')" ], + [ + "STORE_NAME", + "RT" + ], + [ + "STORE_NAME", + "IPYTHON_FILE_PATH" + ], + [ + "STORE_NAME", + "FILE_SENTINEL_NAME" + ], [ "LOAD_NAME", "PY2" @@ -71,18 +199,70 @@ "LOAD_NAME", "unicode" ], + [ + "STORE_NAME", + "Text" + ], [ "LOAD_NAME", "str" ], + [ + "STORE_NAME", + "Text" + ], + [ + "STORE_NAME", + "def path_leaf(path):\n # type: (str) -> str\n # http://stackoverflow.com/a/8384788/2482744\n head, tail = ntpath.split(path)\n return tail or ntpath.basename(head)" + ], + [ + "STORE_NAME", + "def common_ancestor(paths):\n # type: (List[str]) -> str\n \"\"\"\n Returns a path to a directory that contains all the given absolute paths\n \"\"\"\n prefix = os.path.commonprefix(paths)\n\n # Ensure that the prefix doesn't end in part of the name of a file/directory\n prefix = ntpath.split(prefix)[0]\n\n # Ensure that it ends with a slash\n first_char_after = paths[0][len(prefix)]\n if first_char_after in r'\\/':\n prefix += first_char_after\n\n return prefix" + ], + [ + "STORE_NAME", + "def short_path(path, all_paths):\n # type: (str, List[str]) -> str\n if path == IPYTHON_FILE_PATH:\n return path\n\n all_paths = [f for f in all_paths\n if f != IPYTHON_FILE_PATH]\n prefix = common_ancestor(all_paths)\n if prefix in r'\\/':\n prefix = ''\n return strip_required_prefix(path, prefix) or path_leaf(path)" + ], + [ + "STORE_NAME", + "def fix_abs_path(path):\n if path == IPYTHON_FILE_PATH:\n return path\n if os.path.sep == '/' and not path.startswith('/'):\n path = '/' + path\n return path" + ], [ "LOAD_NAME", "PY2" ], + [ + "STORE_NAME", + " def correct_type(obj):\n \"\"\"\n Returns the correct type of obj, regardless of __class__ assignment\n or old-style classes:\n\n >>> class A:\n ... pass\n ...\n ...\n ... class B(object):\n ... pass\n ...\n ...\n ... class C(object):\n ... __class__ = A\n ...\n >>> correct_type(A()) is A\n True\n >>> correct_type(B()) is B\n True\n >>> correct_type(C()) is C\n True\n \"\"\"\n t = type(obj)\n # noinspection PyUnresolvedReferences\n if t is types.InstanceType:\n return obj.__class__\n return t" + ], [ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "correct_type" + ], + [ + "STORE_NAME", + "def of_type(type_or_tuple, iterable):\n # type: (Union[type, Tuple[Union[type, tuple], ...]], Iterable[Any]) -> Iterator[Any]\n return (x for x in iterable if isinstance(x, type_or_tuple))" + ], + [ + "STORE_NAME", + "def safe_next(it):\n # type: (Iterator[T]) -> T\n \"\"\"\n next() can raise a StopIteration which can cause strange bugs inside generators.\n \"\"\"\n try:\n return next(it)\n except StopIteration as e:\n raise_from(RuntimeError, e)\n raise" + ], + [ + "STORE_NAME", + "def one_or_none(expression):\n \"\"\"Performs a one_or_none on a sqlalchemy expression.\"\"\"\n if hasattr(expression, 'one_or_none'):\n return expression.one_or_none()\n result = expression.all()\n if len(result) == 0:\n return None\n elif len(result) == 1:\n return result[0]\n else:\n raise Exception(\"There is more than one item returned for the supplied filter\")" + ], + [ + "STORE_NAME", + "def flatten_list(lst):\n result = []\n for x in lst:\n if isinstance(x, list):\n result.extend(flatten_list(x))\n else:\n result.append(x)\n return result" + ], + [ + "STORE_NAME", + "def is_lambda(f):\n try:\n code = f.__code__\n except AttributeError:\n return False\n return code.co_name == (lambda: 0).__code__.co_name" + ], [ "LOAD_NAME", "json" @@ -91,10 +271,58 @@ "LOAD_ATTR", "json.JSONEncoder" ], + [ + "CALL_FUNCTION", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "from tokenize import open as open_with_encoding_check" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + " def open_with_encoding_check(filename): # type: ignore\n \"\"\"Open a file in read only mode using the encoding detected by\n detect_encoding().\n \"\"\"\n fp = io.open(filename, 'rb')\n try:\n encoding, lines = detect_encoding(fp.readline)\n fp.seek(0)\n text = io.TextIOWrapper(fp, encoding, line_buffering=True)\n text.mode = 'r'\n return text\n except:\n fp.close()\n raise" + ], + [ + "STORE_NAME", + "def read_source_file(filename):\n from lib2to3.pgen2.tokenize import cookie_re\n\n if filename.endswith('.pyc'):\n filename = filename[:-1]\n\n with open_with_encoding_check(filename) as f:\n return ''.join([\n '\\n' if i < 2 and cookie_re.match(line)\n else line\n for i, line in enumerate(f)\n ])" + ], + [ + "STORE_NAME", + "def source_without_decorators(tokens, function_node):\n def_token = safe_next(t for t in tokens.get_tokens(function_node)\n if t.string == 'def' and t.type == token.NAME)\n\n startpos = def_token.startpos\n source = tokens.text[startpos:function_node.last_token.endpos].rstrip()\n assert source.startswith('def')\n\n return startpos, source" + ], + [ + "STORE_NAME", + "def prn(*args):\n for arg in args:\n print(arg)\n if len(args) == 1:\n return args[0]\n return args" + ], + [ + "STORE_NAME", + "def is_ipython_cell(filename):\n return filename.startswith(' str\n # http://stackoverflow.com/a/8384788/2482744\n head, tail = ntpath.split(path)\n return tail or ntpath.basename(head)" + ], + [ + "STORE_NAME", + "def common_ancestor(paths):\n # type: (List[str]) -> str\n \"\"\"\n Returns a path to a directory that contains all the given absolute paths\n \"\"\"\n prefix = os.path.commonprefix(paths)\n\n # Ensure that the prefix doesn't end in part of the name of a file/directory\n prefix = ntpath.split(prefix)[0]\n\n # Ensure that it ends with a slash\n first_char_after = paths[0][len(prefix)]\n if first_char_after in r'\\/':\n prefix += first_char_after\n\n return prefix" + ], + [ + "STORE_NAME", + "def short_path(path, all_paths):\n # type: (str, List[str]) -> str\n if path == IPYTHON_FILE_PATH:\n return path\n\n all_paths = [f for f in all_paths\n if f != IPYTHON_FILE_PATH]\n prefix = common_ancestor(all_paths)\n if prefix in r'\\/':\n prefix = ''\n return strip_required_prefix(path, prefix) or path_leaf(path)" + ], + [ + "STORE_NAME", + "def fix_abs_path(path):\n if path == IPYTHON_FILE_PATH:\n return path\n if os.path.sep == '/' and not path.startswith('/'):\n path = '/' + path\n return path" + ], [ "LOAD_NAME", "PY2" ], + [ + "STORE_NAME", + " def correct_type(obj):\n \"\"\"\n Returns the correct type of obj, regardless of __class__ assignment\n or old-style classes:\n\n >>> class A:\n ... pass\n ...\n ...\n ... class B(object):\n ... pass\n ...\n ...\n ... class C(object):\n ... __class__ = A\n ...\n >>> correct_type(A()) is A\n True\n >>> correct_type(B()) is B\n True\n >>> correct_type(C()) is C\n True\n \"\"\"\n t = type(obj)\n # noinspection PyUnresolvedReferences\n if t is types.InstanceType:\n return obj.__class__\n return t" + ], [ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "correct_type" + ], + [ + "STORE_NAME", + "def of_type(type_or_tuple, iterable):\n # type: (Union[type, Tuple[Union[type, tuple], ...]], Iterable[Any]) -> Iterator[Any]\n return (x for x in iterable if isinstance(x, type_or_tuple))" + ], + [ + "STORE_NAME", + "def safe_next(it):\n # type: (Iterator[T]) -> T\n \"\"\"\n next() can raise a StopIteration which can cause strange bugs inside generators.\n \"\"\"\n try:\n return next(it)\n except StopIteration as e:\n raise_from(RuntimeError, e)\n raise" + ], + [ + "STORE_NAME", + "def one_or_none(expression):\n \"\"\"Performs a one_or_none on a sqlalchemy expression.\"\"\"\n if hasattr(expression, 'one_or_none'):\n return expression.one_or_none()\n result = expression.all()\n if len(result) == 0:\n return None\n elif len(result) == 1:\n return result[0]\n else:\n raise Exception(\"There is more than one item returned for the supplied filter\")" + ], + [ + "STORE_NAME", + "def flatten_list(lst):\n result = []\n for x in lst:\n if isinstance(x, list):\n result.extend(flatten_list(x))\n else:\n result.append(x)\n return result" + ], + [ + "STORE_NAME", + "def is_lambda(f):\n try:\n code = f.__code__\n except AttributeError:\n return False\n return code.co_name == (lambda: 0).__code__.co_name" + ], [ "LOAD_NAME", "json" @@ -91,10 +271,58 @@ "LOAD_ATTR", "json.JSONEncoder" ], + [ + "CALL_FUNCTION", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "from tokenize import open as open_with_encoding_check" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + " def open_with_encoding_check(filename): # type: ignore\n \"\"\"Open a file in read only mode using the encoding detected by\n detect_encoding().\n \"\"\"\n fp = io.open(filename, 'rb')\n try:\n encoding, lines = detect_encoding(fp.readline)\n fp.seek(0)\n text = io.TextIOWrapper(fp, encoding, line_buffering=True)\n text.mode = 'r'\n return text\n except:\n fp.close()\n raise" + ], + [ + "STORE_NAME", + "def read_source_file(filename):\n from lib2to3.pgen2.tokenize import cookie_re\n\n if filename.endswith('.pyc'):\n filename = filename[:-1]\n\n with open_with_encoding_check(filename) as f:\n return ''.join([\n '\\n' if i < 2 and cookie_re.match(line)\n else line\n for i, line in enumerate(f)\n ])" + ], + [ + "STORE_NAME", + "def source_without_decorators(tokens, function_node):\n def_token = safe_next(t for t in tokens.get_tokens(function_node)\n if t.string == 'def' and t.type == token.NAME)\n\n startpos = def_token.startpos\n source = tokens.text[startpos:function_node.last_token.endpos].rstrip()\n assert source.startswith('def')\n\n return startpos, source" + ], + [ + "STORE_NAME", + "def prn(*args):\n for arg in args:\n print(arg)\n if len(args) == 1:\n return args[0]\n return args" + ], + [ + "STORE_NAME", + "def is_ipython_cell(filename):\n return filename.startswith(' str\n # http://stackoverflow.com/a/8384788/2482744\n head, tail = ntpath.split(path)\n return tail or ntpath.basename(head)" + ], + [ + "STORE_NAME", + "def common_ancestor(paths):\n # type: (List[str]) -> str\n \"\"\"\n Returns a path to a directory that contains all the given absolute paths\n \"\"\"\n prefix = os.path.commonprefix(paths)\n\n # Ensure that the prefix doesn't end in part of the name of a file/directory\n prefix = ntpath.split(prefix)[0]\n\n # Ensure that it ends with a slash\n first_char_after = paths[0][len(prefix)]\n if first_char_after in r'\\/':\n prefix += first_char_after\n\n return prefix" + ], + [ + "STORE_NAME", + "def short_path(path, all_paths):\n # type: (str, List[str]) -> str\n if path == IPYTHON_FILE_PATH:\n return path\n\n all_paths = [f for f in all_paths\n if f != IPYTHON_FILE_PATH]\n prefix = common_ancestor(all_paths)\n if prefix in r'\\/':\n prefix = ''\n return strip_required_prefix(path, prefix) or path_leaf(path)" + ], + [ + "STORE_NAME", + "def fix_abs_path(path):\n if path == IPYTHON_FILE_PATH:\n return path\n if os.path.sep == '/' and not path.startswith('/'):\n path = '/' + path\n return path" + ], [ "LOAD_NAME", "PY2" ], + [ + "STORE_NAME", + " def correct_type(obj):\n \"\"\"\n Returns the correct type of obj, regardless of __class__ assignment\n or old-style classes:\n\n >>> class A:\n ... pass\n ...\n ...\n ... class B(object):\n ... pass\n ...\n ...\n ... class C(object):\n ... __class__ = A\n ...\n >>> correct_type(A()) is A\n True\n >>> correct_type(B()) is B\n True\n >>> correct_type(C()) is C\n True\n \"\"\"\n t = type(obj)\n # noinspection PyUnresolvedReferences\n if t is types.InstanceType:\n return obj.__class__\n return t" + ], [ "LOAD_NAME", "type" ], + [ + "STORE_NAME", + "correct_type" + ], + [ + "STORE_NAME", + "def of_type(type_or_tuple, iterable):\n # type: (Union[type, Tuple[Union[type, tuple], ...]], Iterable[Any]) -> Iterator[Any]\n return (x for x in iterable if isinstance(x, type_or_tuple))" + ], + [ + "STORE_NAME", + "def safe_next(it):\n # type: (Iterator[T]) -> T\n \"\"\"\n next() can raise a StopIteration which can cause strange bugs inside generators.\n \"\"\"\n try:\n return next(it)\n except StopIteration as e:\n raise_from(RuntimeError, e)\n raise" + ], + [ + "STORE_NAME", + "def one_or_none(expression):\n \"\"\"Performs a one_or_none on a sqlalchemy expression.\"\"\"\n if hasattr(expression, 'one_or_none'):\n return expression.one_or_none()\n result = expression.all()\n if len(result) == 0:\n return None\n elif len(result) == 1:\n return result[0]\n else:\n raise Exception(\"There is more than one item returned for the supplied filter\")" + ], + [ + "STORE_NAME", + "def flatten_list(lst):\n result = []\n for x in lst:\n if isinstance(x, list):\n result.extend(flatten_list(x))\n else:\n result.append(x)\n return result" + ], + [ + "STORE_NAME", + "def is_lambda(f):\n try:\n code = f.__code__\n except AttributeError:\n return False\n return code.co_name == (lambda: 0).__code__.co_name" + ], [ "LOAD_NAME", "json" @@ -91,10 +271,58 @@ "LOAD_ATTR", "json.JSONEncoder" ], + [ + "CALL_FUNCTION", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "class ProtocolEncoder(json.JSONEncoder):\n def default(self, o):\n try:\n method = o.as_json\n except AttributeError:\n return super(ProtocolEncoder, self).default(o)\n else:\n return method()" + ], + [ + "STORE_NAME", + "from tokenize import open as open_with_encoding_check" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from lib2to3.pgen2.tokenize import detect_encoding" + ], + [ + "STORE_NAME", + "import io" + ], + [ + "STORE_NAME", + " def open_with_encoding_check(filename): # type: ignore\n \"\"\"Open a file in read only mode using the encoding detected by\n detect_encoding().\n \"\"\"\n fp = io.open(filename, 'rb')\n try:\n encoding, lines = detect_encoding(fp.readline)\n fp.seek(0)\n text = io.TextIOWrapper(fp, encoding, line_buffering=True)\n text.mode = 'r'\n return text\n except:\n fp.close()\n raise" + ], + [ + "STORE_NAME", + "def read_source_file(filename):\n from lib2to3.pgen2.tokenize import cookie_re\n\n if filename.endswith('.pyc'):\n filename = filename[:-1]\n\n with open_with_encoding_check(filename) as f:\n return ''.join([\n '\\n' if i < 2 and cookie_re.match(line)\n else line\n for i, line in enumerate(f)\n ])" + ], + [ + "STORE_NAME", + "def source_without_decorators(tokens, function_node):\n def_token = safe_next(t for t in tokens.get_tokens(function_node)\n if t.string == 'def' and t.type == token.NAME)\n\n startpos = def_token.startpos\n source = tokens.text[startpos:function_node.last_token.endpos].rstrip()\n assert source.startswith('def')\n\n return startpos, source" + ], + [ + "STORE_NAME", + "def prn(*args):\n for arg in args:\n print(arg)\n if len(args) == 1:\n return args[0]\n return args" + ], + [ + "STORE_NAME", + "def is_ipython_cell(filename):\n return filename.startswith(' max_length:\n left = (max_length - len(middle)) // 2\n right = max_length - len(middle) - left\n seq = seq[:left] + middle + seq[-right:]\n return seq" + ], + [ + "STORE_NAME", + "def truncate_string(string, max_length):\n return truncate(string, max_length, '...')" + ], + [ + "STORE_NAME", + "def truncate_list(lst, max_length):\n return truncate(lst, max_length, ['...'])" + ], + [ + "STORE_NAME", + "def ensure_tuple(x, split=False):\n if split and isinstance(x, six.string_types):\n x = x.replace(',', ' ').split()\n if not isinstance(x, (list, set, tuple)):\n x = (x,)\n return tuple(x)" + ], + [ + "STORE_NAME", + "def short_filename(code):\n result = os.path.basename(code.co_filename)\n if result.endswith('.pyc'):\n result = result[:-1]\n return result" + ], + [ + "STORE_NAME", + "def is_comprehension_frame(frame):\n return frame.f_code.co_name in ('', '', '')" + ], + [ + "STORE_NAME", + "def needs_parentheses(source):\n def code(s):\n return compile(s.format(source), '', 'eval').co_code\n\n try:\n without_parens = code('{}.x')\n except SyntaxError:\n # Likely a multiline expression that needs parentheses to be valid\n code('({})')\n return True\n else:\n return without_parens != code('({}).x')" + ], + [ + "STORE_NAME", + "def with_needed_parentheses(source):\n if needs_parentheses(source):\n return '({})'.format(source)\n else:\n return source" + ], + [ + "STORE_NAME", + "REPR_TARGET_LENGTH" + ], + [ + "STORE_NAME", + "def my_cheap_repr(x):\n return cheap_repr(x, target_length=REPR_TARGET_LENGTH)" + ], [ "LOAD_NAME", "dict" ], + [ + "CALL_FUNCTION", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "def optional_numeric_label(i, lst):\n if len(lst) == 1:\n return ''\n else:\n return ' ' + str(i + 1)" + ], + [ + "STORE_NAME", + "def is_pathlike(x):\n if hasattr(os, 'PathLike'):\n return isinstance(x, os.PathLike)\n\n return (\n hasattr(x, '__fspath__') or\n # Make a concession for older `pathlib` versions:\n (hasattr(x, 'open') and\n 'path' in x.__class__.__name__.lower())\n )" + ], [ "LOAD_NAME", "inspect" @@ -59,10 +159,18 @@ "LOAD_ATTR", "inspect.iscoroutinefunction" ], + [ + "STORE_NAME", + "iscoroutinefunction" + ], [ "LOAD_NAME", "AttributeError" ], + [ + "STORE_NAME", + " def iscoroutinefunction(_):\n return False" + ], [ "LOAD_NAME", "ast" @@ -71,6 +179,10 @@ "LOAD_ATTR", "ast.Try" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "AttributeError" @@ -83,6 +195,10 @@ "LOAD_ATTR", "ast.TryExcept" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "__import__" @@ -91,6 +207,10 @@ "CALL_FUNCTION", "__import__(\"__builtin__\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ImportError" @@ -103,6 +223,10 @@ "CALL_FUNCTION", "__import__(\"builtins\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ast" @@ -111,18 +235,54 @@ "LOAD_ATTR", "ast.FormattedValue" ], + [ + "STORE_NAME", + "FormattedValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + "def no_args_decorator(args, kwargs):\n return len(args) == 1 and inspect.isfunction(args[0]) and not kwargs" + ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from backports.functools_lru_cache import lru_cache" + ], [ "LOAD_NAME", "str" ], + [ + "CALL_FUNCTION", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -131,6 +291,18 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -143,6 +315,10 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], [ "LOAD_METHOD", "''.join" @@ -151,10 +327,22 @@ "LOAD_FAST", "s" ], + [ + "CALL_FUNCTION", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], [ "CALL_METHOD", "''.join(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" ], + [ + "LOAD_FAST", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -167,6 +355,14 @@ "CALL_FUNCTION", "ord(c)" ], + [ + "COMPARE_OP", + "0 < ord(c) < 256" + ], + [ + "COMPARE_OP", + "0 < ord(c) < 256" + ], [ "LOAD_FAST", "c" @@ -215,6 +411,10 @@ "BINARY_FLOOR_DIVIDE", "(max_length - len(middle)) // 2" ], + [ + "STORE_FAST", + "left" + ], [ "LOAD_FAST", "max_length" @@ -243,6 +443,10 @@ "BINARY_SUBTRACT", "max_length - len(middle) - left" ], + [ + "STORE_FAST", + "right" + ], [ "LOAD_FAST", "seq" @@ -283,6 +487,10 @@ "BINARY_ADD", "seq[:left] + middle + seq[-right:]" ], + [ + "STORE_FAST", + "seq" + ], [ "LOAD_FAST", "seq" @@ -363,6 +571,10 @@ "CALL_METHOD", "x.replace(',', ' ').split()" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "isinstance" @@ -391,6 +603,10 @@ "LOAD_FAST", "x" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tuple" @@ -427,6 +643,10 @@ "CALL_METHOD", "os.path.basename(code.co_filename)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -447,6 +667,10 @@ "BINARY_SUBSCR", "result[:-1]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -467,6 +691,10 @@ "CONTAINS_OP", "frame.f_code.co_name in ('', '', '')" ], + [ + "STORE_FAST", + " def code(s):\n return compile(s.format(source), '', 'eval').co_code" + ], [ "LOAD_FAST", "code" @@ -475,6 +703,10 @@ "CALL_FUNCTION", "code('{}.x')" ], + [ + "STORE_FAST", + "without_parens" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -575,6 +807,26 @@ "CALL_FUNCTION_KW", "cheap_repr(x, target_length=REPR_TARGET_LENGTH)" ], + [ + "LOAD_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + " def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory" + ], + [ + "STORE_NAME", + " def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], [ "LOAD_GLOBAL", "super" @@ -627,6 +879,10 @@ "CALL_METHOD", "self.factory(key)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -759,6 +1015,18 @@ "CONTAINS_OP", "'path' in x.__class__.__name__.lower()" ], + [ + "LOAD_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], [ "LOAD_GLOBAL", "len" @@ -803,10 +1071,38 @@ "UNARY_NOT", "not kwargs" ], + [ + "LOAD_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return self" + ], [ "LOAD_FAST", "self" ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_FAST", "length" @@ -899,6 +1195,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -931,6 +1231,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -939,6 +1247,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -955,6 +1267,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -991,6 +1307,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -1007,6 +1327,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" diff --git a/tests/sample_results/utils2-py-3.8.json b/tests/sample_results/utils2-py-3.8.json index ef142c4..08c6639 100644 --- a/tests/sample_results/utils2-py-3.8.json +++ b/tests/sample_results/utils2-py-3.8.json @@ -1,4 +1,36 @@ [ + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from itertools import chain" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], [ "LOAD_NAME", "sys" @@ -35,6 +67,10 @@ "COMPARE_OP", "sys.version_info[:2] in [(3, 4), (3, 8)]" ], + [ + "STORE_NAME", + "NO_ASTTOKENS" + ], [ "LOAD_NAME", "IOError" @@ -47,10 +83,74 @@ "LOAD_NAME", "ValueError" ], + [ + "STORE_NAME", + "file_reading_errors" + ], + [ + "STORE_NAME", + "def shitcode(s):\n return ''.join(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], + [ + "STORE_NAME", + "def truncate(seq, max_length, middle):\n if len(seq) > max_length:\n left = (max_length - len(middle)) // 2\n right = max_length - len(middle) - left\n seq = seq[:left] + middle + seq[-right:]\n return seq" + ], + [ + "STORE_NAME", + "def truncate_string(string, max_length):\n return truncate(string, max_length, '...')" + ], + [ + "STORE_NAME", + "def truncate_list(lst, max_length):\n return truncate(lst, max_length, ['...'])" + ], + [ + "STORE_NAME", + "def ensure_tuple(x, split=False):\n if split and isinstance(x, six.string_types):\n x = x.replace(',', ' ').split()\n if not isinstance(x, (list, set, tuple)):\n x = (x,)\n return tuple(x)" + ], + [ + "STORE_NAME", + "def short_filename(code):\n result = os.path.basename(code.co_filename)\n if result.endswith('.pyc'):\n result = result[:-1]\n return result" + ], + [ + "STORE_NAME", + "def is_comprehension_frame(frame):\n return frame.f_code.co_name in ('', '', '')" + ], + [ + "STORE_NAME", + "def needs_parentheses(source):\n def code(s):\n return compile(s.format(source), '', 'eval').co_code\n\n try:\n without_parens = code('{}.x')\n except SyntaxError:\n # Likely a multiline expression that needs parentheses to be valid\n code('({})')\n return True\n else:\n return without_parens != code('({}).x')" + ], + [ + "STORE_NAME", + "def with_needed_parentheses(source):\n if needs_parentheses(source):\n return '({})'.format(source)\n else:\n return source" + ], + [ + "STORE_NAME", + "REPR_TARGET_LENGTH" + ], + [ + "STORE_NAME", + "def my_cheap_repr(x):\n return cheap_repr(x, target_length=REPR_TARGET_LENGTH)" + ], [ "LOAD_NAME", "dict" ], + [ + "CALL_FUNCTION", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "def optional_numeric_label(i, lst):\n if len(lst) == 1:\n return ''\n else:\n return ' ' + str(i + 1)" + ], + [ + "STORE_NAME", + "def is_pathlike(x):\n if hasattr(os, 'PathLike'):\n return isinstance(x, os.PathLike)\n\n return (\n hasattr(x, '__fspath__') or\n # Make a concession for older `pathlib` versions:\n (hasattr(x, 'open') and\n 'path' in x.__class__.__name__.lower())\n )" + ], [ "LOAD_NAME", "inspect" @@ -59,10 +159,18 @@ "LOAD_ATTR", "inspect.iscoroutinefunction" ], + [ + "STORE_NAME", + "iscoroutinefunction" + ], [ "LOAD_NAME", "AttributeError" ], + [ + "STORE_NAME", + " def iscoroutinefunction(_):\n return False" + ], [ "LOAD_NAME", "ast" @@ -71,6 +179,10 @@ "LOAD_ATTR", "ast.Try" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "AttributeError" @@ -83,6 +195,10 @@ "LOAD_ATTR", "ast.TryExcept" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "__import__" @@ -91,6 +207,10 @@ "CALL_FUNCTION", "__import__(\"__builtin__\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ImportError" @@ -103,6 +223,10 @@ "CALL_FUNCTION", "__import__(\"builtins\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ast" @@ -111,18 +235,54 @@ "LOAD_ATTR", "ast.FormattedValue" ], + [ + "STORE_NAME", + "FormattedValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + "def no_args_decorator(args, kwargs):\n return len(args) == 1 and inspect.isfunction(args[0]) and not kwargs" + ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from backports.functools_lru_cache import lru_cache" + ], [ "LOAD_NAME", "str" ], + [ + "CALL_FUNCTION", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -131,6 +291,18 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -143,6 +315,10 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], [ "LOAD_METHOD", "''.join" @@ -151,10 +327,22 @@ "LOAD_FAST", "s" ], + [ + "CALL_FUNCTION", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], [ "CALL_METHOD", "''.join(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" ], + [ + "LOAD_FAST", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -215,6 +403,10 @@ "BINARY_FLOOR_DIVIDE", "(max_length - len(middle)) // 2" ], + [ + "STORE_FAST", + "left" + ], [ "LOAD_FAST", "max_length" @@ -243,6 +435,10 @@ "BINARY_SUBTRACT", "max_length - len(middle) - left" ], + [ + "STORE_FAST", + "right" + ], [ "LOAD_FAST", "seq" @@ -283,6 +479,10 @@ "BINARY_ADD", "seq[:left] + middle + seq[-right:]" ], + [ + "STORE_FAST", + "seq" + ], [ "LOAD_FAST", "seq" @@ -363,6 +563,10 @@ "CALL_METHOD", "x.replace(',', ' ').split()" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "isinstance" @@ -391,6 +595,10 @@ "LOAD_FAST", "x" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tuple" @@ -427,6 +635,10 @@ "CALL_METHOD", "os.path.basename(code.co_filename)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -447,6 +659,10 @@ "BINARY_SUBSCR", "result[:-1]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -467,6 +683,10 @@ "COMPARE_OP", "frame.f_code.co_name in ('', '', '')" ], + [ + "STORE_FAST", + " def code(s):\n return compile(s.format(source), '', 'eval').co_code" + ], [ "LOAD_FAST", "code" @@ -475,6 +695,10 @@ "CALL_FUNCTION", "code('{}.x')" ], + [ + "STORE_FAST", + "without_parens" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -575,6 +799,26 @@ "CALL_FUNCTION_KW", "cheap_repr(x, target_length=REPR_TARGET_LENGTH)" ], + [ + "LOAD_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + " def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory" + ], + [ + "STORE_NAME", + " def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], [ "LOAD_GLOBAL", "super" @@ -627,6 +871,10 @@ "CALL_METHOD", "self.factory(key)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -759,6 +1007,18 @@ "COMPARE_OP", "'path' in x.__class__.__name__.lower()" ], + [ + "LOAD_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], [ "LOAD_GLOBAL", "len" @@ -803,10 +1063,38 @@ "UNARY_NOT", "not kwargs" ], + [ + "LOAD_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return self" + ], [ "LOAD_FAST", "self" ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_FAST", "length" @@ -899,6 +1187,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -931,6 +1223,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -939,6 +1239,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -955,6 +1259,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -991,6 +1299,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -1007,6 +1319,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" diff --git a/tests/sample_results/utils2-py-3.9.json b/tests/sample_results/utils2-py-3.9.json index 3c7228e..736ceb8 100644 --- a/tests/sample_results/utils2-py-3.9.json +++ b/tests/sample_results/utils2-py-3.9.json @@ -1,4 +1,36 @@ [ + [ + "STORE_NAME", + "import ast" + ], + [ + "STORE_NAME", + "import inspect" + ], + [ + "STORE_NAME", + "import os" + ], + [ + "STORE_NAME", + "import sys" + ], + [ + "STORE_NAME", + "from itertools import chain" + ], + [ + "STORE_NAME", + "import six" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], + [ + "STORE_NAME", + "from cheap_repr import cheap_repr, try_register_repr" + ], [ "LOAD_NAME", "sys" @@ -35,6 +67,10 @@ "CONTAINS_OP", "sys.version_info[:2] in [(3, 4), (3, 8)]" ], + [ + "STORE_NAME", + "NO_ASTTOKENS" + ], [ "LOAD_NAME", "IOError" @@ -47,10 +83,74 @@ "LOAD_NAME", "ValueError" ], + [ + "STORE_NAME", + "file_reading_errors" + ], + [ + "STORE_NAME", + "def shitcode(s):\n return ''.join(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], + [ + "STORE_NAME", + "def truncate(seq, max_length, middle):\n if len(seq) > max_length:\n left = (max_length - len(middle)) // 2\n right = max_length - len(middle) - left\n seq = seq[:left] + middle + seq[-right:]\n return seq" + ], + [ + "STORE_NAME", + "def truncate_string(string, max_length):\n return truncate(string, max_length, '...')" + ], + [ + "STORE_NAME", + "def truncate_list(lst, max_length):\n return truncate(lst, max_length, ['...'])" + ], + [ + "STORE_NAME", + "def ensure_tuple(x, split=False):\n if split and isinstance(x, six.string_types):\n x = x.replace(',', ' ').split()\n if not isinstance(x, (list, set, tuple)):\n x = (x,)\n return tuple(x)" + ], + [ + "STORE_NAME", + "def short_filename(code):\n result = os.path.basename(code.co_filename)\n if result.endswith('.pyc'):\n result = result[:-1]\n return result" + ], + [ + "STORE_NAME", + "def is_comprehension_frame(frame):\n return frame.f_code.co_name in ('', '', '')" + ], + [ + "STORE_NAME", + "def needs_parentheses(source):\n def code(s):\n return compile(s.format(source), '', 'eval').co_code\n\n try:\n without_parens = code('{}.x')\n except SyntaxError:\n # Likely a multiline expression that needs parentheses to be valid\n code('({})')\n return True\n else:\n return without_parens != code('({}).x')" + ], + [ + "STORE_NAME", + "def with_needed_parentheses(source):\n if needs_parentheses(source):\n return '({})'.format(source)\n else:\n return source" + ], + [ + "STORE_NAME", + "REPR_TARGET_LENGTH" + ], + [ + "STORE_NAME", + "def my_cheap_repr(x):\n return cheap_repr(x, target_length=REPR_TARGET_LENGTH)" + ], [ "LOAD_NAME", "dict" ], + [ + "CALL_FUNCTION", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "def optional_numeric_label(i, lst):\n if len(lst) == 1:\n return ''\n else:\n return ' ' + str(i + 1)" + ], + [ + "STORE_NAME", + "def is_pathlike(x):\n if hasattr(os, 'PathLike'):\n return isinstance(x, os.PathLike)\n\n return (\n hasattr(x, '__fspath__') or\n # Make a concession for older `pathlib` versions:\n (hasattr(x, 'open') and\n 'path' in x.__class__.__name__.lower())\n )" + ], [ "LOAD_NAME", "inspect" @@ -59,10 +159,18 @@ "LOAD_ATTR", "inspect.iscoroutinefunction" ], + [ + "STORE_NAME", + "iscoroutinefunction" + ], [ "LOAD_NAME", "AttributeError" ], + [ + "STORE_NAME", + " def iscoroutinefunction(_):\n return False" + ], [ "LOAD_NAME", "ast" @@ -71,6 +179,10 @@ "LOAD_ATTR", "ast.Try" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "AttributeError" @@ -83,6 +195,10 @@ "LOAD_ATTR", "ast.TryExcept" ], + [ + "STORE_NAME", + "try_statement" + ], [ "LOAD_NAME", "__import__" @@ -91,6 +207,10 @@ "CALL_FUNCTION", "__import__(\"__builtin__\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ImportError" @@ -103,6 +223,10 @@ "CALL_FUNCTION", "__import__(\"builtins\")" ], + [ + "STORE_NAME", + "builtins" + ], [ "LOAD_NAME", "ast" @@ -111,18 +235,54 @@ "LOAD_ATTR", "ast.FormattedValue" ], + [ + "STORE_NAME", + "FormattedValue" + ], [ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + "def no_args_decorator(args, kwargs):\n return len(args) == 1 and inspect.isfunction(args[0]) and not kwargs" + ], + [ + "STORE_NAME", + "from functools import lru_cache" + ], [ "LOAD_NAME", "ImportError" ], + [ + "STORE_NAME", + "from backports.functools_lru_cache import lru_cache" + ], [ "LOAD_NAME", "str" ], + [ + "CALL_FUNCTION", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "from django.db.models import QuerySet" + ], [ "LOAD_NAME", "ImportError" @@ -131,6 +291,18 @@ "LOAD_NAME", "object" ], + [ + "CALL_FUNCTION", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + "def _sample_indices(length, max_length):\n if length <= max_length + 2:\n return range(length)\n else:\n return chain(range(max_length // 2),\n range(length - max_length // 2,\n length))" + ], [ "LOAD_NAME", "try_register_repr" @@ -143,6 +315,10 @@ "CALL_FUNCTION", "try_register_repr('pandas', 'Series')" ], + [ + "STORE_NAME", + "@try_register_repr('pandas', 'Series')\ndef _repr_series_one_line(x, helper):\n n = len(x)\n if n == 0:\n return repr(x)\n newlevel = helper.level - 1\n pieces = []\n maxparts = _repr_series_one_line.maxparts\n for i in _sample_indices(n, maxparts):\n k = x.index[i:i + 1].format(sparsify=False)[0]\n v = x.iloc[i]\n pieces.append('%s = %s' % (k, cheap_repr(v, newlevel)))\n if n > maxparts + 2:\n pieces.insert(maxparts // 2, '...')\n return '; '.join(pieces)" + ], [ "LOAD_METHOD", "''.join" @@ -151,10 +327,22 @@ "LOAD_FAST", "s" ], + [ + "CALL_FUNCTION", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], [ "CALL_METHOD", "''.join(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" ], + [ + "LOAD_FAST", + "(\n (c if (0 < ord(c) < 256) else '?') for c in s\n )" + ], + [ + "STORE_FAST", + "c" + ], [ "LOAD_GLOBAL", "ord" @@ -215,6 +403,10 @@ "BINARY_FLOOR_DIVIDE", "(max_length - len(middle)) // 2" ], + [ + "STORE_FAST", + "left" + ], [ "LOAD_FAST", "max_length" @@ -243,6 +435,10 @@ "BINARY_SUBTRACT", "max_length - len(middle) - left" ], + [ + "STORE_FAST", + "right" + ], [ "LOAD_FAST", "seq" @@ -283,6 +479,10 @@ "BINARY_ADD", "seq[:left] + middle + seq[-right:]" ], + [ + "STORE_FAST", + "seq" + ], [ "LOAD_FAST", "seq" @@ -363,6 +563,10 @@ "CALL_METHOD", "x.replace(',', ' ').split()" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "isinstance" @@ -391,6 +595,10 @@ "LOAD_FAST", "x" ], + [ + "STORE_FAST", + "x" + ], [ "LOAD_GLOBAL", "tuple" @@ -427,6 +635,10 @@ "CALL_METHOD", "os.path.basename(code.co_filename)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -447,6 +659,10 @@ "BINARY_SUBSCR", "result[:-1]" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "result" @@ -467,6 +683,10 @@ "CONTAINS_OP", "frame.f_code.co_name in ('', '', '')" ], + [ + "STORE_FAST", + " def code(s):\n return compile(s.format(source), '', 'eval').co_code" + ], [ "LOAD_FAST", "code" @@ -475,6 +695,10 @@ "CALL_FUNCTION", "code('{}.x')" ], + [ + "STORE_FAST", + "without_parens" + ], [ "LOAD_GLOBAL", "SyntaxError" @@ -575,6 +799,26 @@ "CALL_FUNCTION_KW", "cheap_repr(x, target_length=REPR_TARGET_LENGTH)" ], + [ + "LOAD_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + "class ArgDefaultDict(dict):\n def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory\n\n def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], + [ + "STORE_NAME", + " def __init__(self, factory):\n super(ArgDefaultDict, self).__init__()\n self.factory = factory" + ], + [ + "STORE_NAME", + " def __missing__(self, key):\n result = self[key] = self.factory(key)\n return result" + ], [ "LOAD_GLOBAL", "super" @@ -627,6 +871,10 @@ "CALL_METHOD", "self.factory(key)" ], + [ + "STORE_FAST", + "result" + ], [ "LOAD_FAST", "self" @@ -759,6 +1007,18 @@ "CONTAINS_OP", "'path' in x.__class__.__name__.lower()" ], + [ + "LOAD_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], + [ + "STORE_NAME", + " class FormattedValue(object):\n pass" + ], [ "LOAD_GLOBAL", "len" @@ -803,10 +1063,38 @@ "UNARY_NOT", "not kwargs" ], + [ + "LOAD_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + "class DirectRepr(str):\n def __repr__(self):\n return self" + ], + [ + "STORE_NAME", + " def __repr__(self):\n return self" + ], [ "LOAD_FAST", "self" ], + [ + "LOAD_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], + [ + "STORE_NAME", + " class QuerySet(object):\n pass" + ], [ "LOAD_FAST", "length" @@ -899,6 +1187,10 @@ "CALL_FUNCTION", "len(x)" ], + [ + "STORE_FAST", + "n" + ], [ "LOAD_FAST", "n" @@ -931,6 +1223,14 @@ "BINARY_SUBTRACT", "helper.level - 1" ], + [ + "STORE_FAST", + "newlevel" + ], + [ + "STORE_FAST", + "pieces" + ], [ "LOAD_GLOBAL", "_repr_series_one_line" @@ -939,6 +1239,10 @@ "LOAD_ATTR", "_repr_series_one_line.maxparts" ], + [ + "STORE_FAST", + "maxparts" + ], [ "LOAD_GLOBAL", "_sample_indices" @@ -955,6 +1259,10 @@ "CALL_FUNCTION", "_sample_indices(n, maxparts)" ], + [ + "STORE_FAST", + "i" + ], [ "LOAD_FAST", "x" @@ -991,6 +1299,10 @@ "BINARY_SUBSCR", "x.index[i:i + 1].format(sparsify=False)[0]" ], + [ + "STORE_FAST", + "k" + ], [ "LOAD_FAST", "x" @@ -1007,6 +1319,10 @@ "BINARY_SUBSCR", "x.iloc[i]" ], + [ + "STORE_FAST", + "v" + ], [ "LOAD_FAST", "pieces" diff --git a/tests/small_samples/011159a09f07b2f1522defc5a799b5a53c7f2c369e143c1ad65c5709fbd10630.py b/tests/small_samples/011159a09f07b2f1522defc5a799b5a53c7f2c369e143c1ad65c5709fbd10630.py new file mode 100644 index 0000000..029663b --- /dev/null +++ b/tests/small_samples/011159a09f07b2f1522defc5a799b5a53c7f2c369e143c1ad65c5709fbd10630.py @@ -0,0 +1,2 @@ +while (t for t in s): + pass \ No newline at end of file diff --git a/tests/small_samples/0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py b/tests/small_samples/0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py index 969d819..6850820 100644 --- a/tests/small_samples/0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py +++ b/tests/small_samples/0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py @@ -16,4 +16,4 @@ def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node): if frame or _tracing_recursively: return None - exc_value \ No newline at end of file + exc_value diff --git a/tests/small_samples/02ae28633a27bf4eac8da873ee1c95bef254726c809f57fb63107a23aad67b4f.py b/tests/small_samples/02ae28633a27bf4eac8da873ee1c95bef254726c809f57fb63107a23aad67b4f.py new file mode 100644 index 0000000..5cf76df --- /dev/null +++ b/tests/small_samples/02ae28633a27bf4eac8da873ee1c95bef254726c809f57fb63107a23aad67b4f.py @@ -0,0 +1,7 @@ +def _get_expected_error_settings_dict(): + try: + pass + except Exception as e: + pass + expected_error_settings_dict + return _EXPECTED_ERROR_SETTINGS_DICT \ No newline at end of file diff --git a/tests/small_samples/0416379ad6fbd141b49fc69dd38eada040fc6fc7a3c21f7be79d633de50472a3.py b/tests/small_samples/0416379ad6fbd141b49fc69dd38eada040fc6fc7a3c21f7be79d633de50472a3.py new file mode 100644 index 0000000..8efe804 --- /dev/null +++ b/tests/small_samples/0416379ad6fbd141b49fc69dd38eada040fc6fc7a3c21f7be79d633de50472a3.py @@ -0,0 +1,7 @@ +def strides_from_shape(): + if layout: + for i in range: + pass + else: + pass + strides \ No newline at end of file diff --git a/tests/small_samples/053317995d54ab43e7fd0d33335f58d4514cf901bc4ed4fbca0ee5e58c2cd703.py b/tests/small_samples/053317995d54ab43e7fd0d33335f58d4514cf901bc4ed4fbca0ee5e58c2cd703.py new file mode 100644 index 0000000..aa2934e --- /dev/null +++ b/tests/small_samples/053317995d54ab43e7fd0d33335f58d4514cf901bc4ed4fbca0ee5e58c2cd703.py @@ -0,0 +1,3 @@ +for start in reversed: + with self: + break \ No newline at end of file diff --git a/tests/small_samples/0710cb55e14e47b2b87fd4d3e2a100a80132b78c37ecdf8bfb715bf04ef9f4b3.py b/tests/small_samples/0710cb55e14e47b2b87fd4d3e2a100a80132b78c37ecdf8bfb715bf04ef9f4b3.py new file mode 100644 index 0000000..9bbe622 --- /dev/null +++ b/tests/small_samples/0710cb55e14e47b2b87fd4d3e2a100a80132b78c37ecdf8bfb715bf04ef9f4b3.py @@ -0,0 +1,10 @@ +try: + compiler_options = CompilerOptions(multi_file=self.multi_file, separate=self.separate) + result = emitmodule.parse_and_typecheck(sources=sources, options=options, compiler_options=compiler_options, groups=groups, alt_lib_path='.') + errors = Errors() + (ir, cfiles) = emitmodule.compile_modules_to_c(result, compiler_options=compiler_options, errors=errors, groups=groups) + if errors.num_errors: + errors.flush_errors() + assert False, 'Compile error' +except CompileError as e: + assert False, 'Compile error' \ No newline at end of file diff --git a/tests/small_samples/0ced2b63b1dd49ff476d6011109a5eeb7bd2870181619c686cd0ec1a0d1db091.py b/tests/small_samples/0ced2b63b1dd49ff476d6011109a5eeb7bd2870181619c686cd0ec1a0d1db091.py new file mode 100644 index 0000000..f34d094 --- /dev/null +++ b/tests/small_samples/0ced2b63b1dd49ff476d6011109a5eeb7bd2870181619c686cd0ec1a0d1db091.py @@ -0,0 +1,7 @@ +def check(): + try: + pass + except NannyNag as nag: + return + finally: + f \ No newline at end of file diff --git a/tests/small_samples/1a16fc51b8c2a11983bced25bfbf31a1cfdd116751d6a22691d34fa293cb8a1f.py b/tests/small_samples/1a16fc51b8c2a11983bced25bfbf31a1cfdd116751d6a22691d34fa293cb8a1f.py new file mode 100644 index 0000000..46ae1fd --- /dev/null +++ b/tests/small_samples/1a16fc51b8c2a11983bced25bfbf31a1cfdd116751d6a22691d34fa293cb8a1f.py @@ -0,0 +1,5 @@ +def testClassdef(): + + @class_decorator + class G: + pass \ No newline at end of file diff --git a/tests/small_samples/1a7c450c636ad31037fcf99695ff6c3730f38bac7c0b3350b867afce3d27a805.py b/tests/small_samples/1a7c450c636ad31037fcf99695ff6c3730f38bac7c0b3350b867afce3d27a805.py new file mode 100644 index 0000000..e9b11d2 --- /dev/null +++ b/tests/small_samples/1a7c450c636ad31037fcf99695ff6c3730f38bac7c0b3350b867afce3d27a805.py @@ -0,0 +1,3 @@ +match (0, 1, 2): + case [*x, 2]: + y = 0 \ No newline at end of file diff --git a/tests/small_samples/1ca248a17a95b161d52577de4f0a82224c35fbb70ab5aedfd73e7f14bcff4930.py b/tests/small_samples/1ca248a17a95b161d52577de4f0a82224c35fbb70ab5aedfd73e7f14bcff4930.py new file mode 100644 index 0000000..3467fcc --- /dev/null +++ b/tests/small_samples/1ca248a17a95b161d52577de4f0a82224c35fbb70ab5aedfd73e7f14bcff4930.py @@ -0,0 +1,20 @@ + +kwargs.pop('timeout', None) +kwargs.pop('raise_on_any_failure', True) +kwargs.pop('delete_snapshots', None) +kwargs.pop('if_modified_since', None) +kwargs.pop('if_unmodified_since', None) +kwargs.pop('if_tags_match_condition', None) +kwargs.update({'raise_on_any_failure': raise_on_any_failure, 'sas': self._query_str.replace('?', '&'), 'timeout': (('&timeout=' + str(timeout)) if timeout else ''), 'path': self.container_name, 'restype': 'restype=container&'}) +[] +for blob in blobs: + _get_blob_name(blob) + self.container_name + try: + BlobClient._generic_delete_blob_options(snapshot=blob.get('snapshot'), delete_snapshots=(delete_snapshots or blob.get('delete_snapshots')), lease=blob.get('lease_id'), if_modified_since=(if_modified_since or blob.get('if_modified_since')), if_unmodified_since=(if_unmodified_since or blob.get('if_unmodified_since')), etag=blob.get('etag'), if_tags_match_condition=(if_tags_match_condition or blob.get('if_tags_match_condition')), match_condition=((blob.get('match_condition') or MatchConditions.IfNotModified) if blob.get('etag') else None), timeout=blob.get('timeout')) + except AttributeError: + BlobClient._generic_delete_blob_options(delete_snapshots=delete_snapshots, if_modified_since=if_modified_since, if_unmodified_since=if_unmodified_since, if_tags_match_condition=if_tags_match_condition) + (query_parameters, header_parameters) = self._generate_delete_blobs_subrequest_options(**options) + HttpRequest('DELETE', '/{}/{}{}'.format(quote(container_name), quote(blob_name, safe='/~'), self._query_str), headers=header_parameters) + req.format_parameters(query_parameters) + reqs.append(req) diff --git a/tests/small_samples/26f461bb5b8489339ce2d7750b300e6ab0614ec1436f8297eead9db01a2525ad.py b/tests/small_samples/26f461bb5b8489339ce2d7750b300e6ab0614ec1436f8297eead9db01a2525ad.py new file mode 100644 index 0000000..d021d38 --- /dev/null +++ b/tests/small_samples/26f461bb5b8489339ce2d7750b300e6ab0614ec1436f8297eead9db01a2525ad.py @@ -0,0 +1,9 @@ +""" +This module provides the generic functionality of tracing code by +modifying its AST. Eventually this will become a separate package. +This is similar to the standard library module bdb, while birdseye +itself would correspond to pdb. +Most of the work is in TreeTracerBase. +""" +while True: + pass \ No newline at end of file diff --git a/tests/small_samples/28209f023b5932dc7079632506d1e450b6b885758428d90feaffa4bcb7892621.py b/tests/small_samples/28209f023b5932dc7079632506d1e450b6b885758428d90feaffa4bcb7892621.py new file mode 100644 index 0000000..609245c --- /dev/null +++ b/tests/small_samples/28209f023b5932dc7079632506d1e450b6b885758428d90feaffa4bcb7892621.py @@ -0,0 +1,6 @@ +def tabnanny(): + with tokenize as f: + try: + pass + except tabnanny as nag: + return False \ No newline at end of file diff --git a/tests/small_samples/2e29ac3de945bcdee56a1e568664fd2462d3cca8a088a63b31c4c94dda3ed9ee.py b/tests/small_samples/2e29ac3de945bcdee56a1e568664fd2462d3cca8a088a63b31c4c94dda3ed9ee.py new file mode 100644 index 0000000..5a04634 --- /dev/null +++ b/tests/small_samples/2e29ac3de945bcdee56a1e568664fd2462d3cca8a088a63b31c4c94dda3ed9ee.py @@ -0,0 +1,3 @@ +def _do_cmp(): + with open, open: + return False \ No newline at end of file diff --git a/tests/small_samples/32fee793ccfb55b0a62c5f09b434458c11c22d58a110ddbeff8ec758fefe295a.py b/tests/small_samples/32fee793ccfb55b0a62c5f09b434458c11c22d58a110ddbeff8ec758fefe295a.py new file mode 100644 index 0000000..3b5589a --- /dev/null +++ b/tests/small_samples/32fee793ccfb55b0a62c5f09b434458c11c22d58a110ddbeff8ec758fefe295a.py @@ -0,0 +1,5 @@ +while True: + if self: + pass + if self: + pass \ No newline at end of file diff --git a/tests/small_samples/33d5577f16330ff69e81d16372be7b3965a301c303b8aa6bea8ae3c4749f6e51.py b/tests/small_samples/33d5577f16330ff69e81d16372be7b3965a301c303b8aa6bea8ae3c4749f6e51.py new file mode 100644 index 0000000..b496200 --- /dev/null +++ b/tests/small_samples/33d5577f16330ff69e81d16372be7b3965a301c303b8aa6bea8ae3c4749f6e51.py @@ -0,0 +1,3 @@ +def _read_gzip_header(): + while True: + pass \ No newline at end of file diff --git a/tests/small_samples/3c18f4a390e55d0777c8304c757fcd36364fad552e5f9e1e2f5a2b98544dd06b.py b/tests/small_samples/3c18f4a390e55d0777c8304c757fcd36364fad552e5f9e1e2f5a2b98544dd06b.py new file mode 100644 index 0000000..c72bad4 --- /dev/null +++ b/tests/small_samples/3c18f4a390e55d0777c8304c757fcd36364fad552e5f9e1e2f5a2b98544dd06b.py @@ -0,0 +1 @@ +assert Mul == Mul in [] \ No newline at end of file diff --git a/tests/small_samples/3db492a3374253de1721927ed06d0b1c4882517375d4320b6b51654a88590f43.py b/tests/small_samples/3db492a3374253de1721927ed06d0b1c4882517375d4320b6b51654a88590f43.py new file mode 100644 index 0000000..d337f89 --- /dev/null +++ b/tests/small_samples/3db492a3374253de1721927ed06d0b1c4882517375d4320b6b51654a88590f43.py @@ -0,0 +1,7 @@ +try: + pass +except WindowsError as e: + pass +else: + pass +self \ No newline at end of file diff --git a/tests/small_samples/3df02cf6e7acbb08f06f30baaef8b5caa3479766573f42b254fb56f395f18af5.py b/tests/small_samples/3df02cf6e7acbb08f06f30baaef8b5caa3479766573f42b254fb56f395f18af5.py new file mode 100644 index 0000000..5908a08 --- /dev/null +++ b/tests/small_samples/3df02cf6e7acbb08f06f30baaef8b5caa3479766573f42b254fb56f395f18af5.py @@ -0,0 +1,2 @@ +[[0, 32], [1, 31], [2, 30], [3, 29], [4, 28], [5, 27], [6, 26], [7, 25], [8, 24], [9, 23], [10, 22], [11, 21], [12, 20], [13, 19], [14, 18], [15, 17], [33, 46], [34, 45], [35, 44], [36, 43], [37, 42], [38, 50], [39, 49], [40, 48], [41, 47], [60, 72], [61, 71], [62, 70], [63, 69], [64, 68], [65, 75], [66, 74], [67, 73], [96, 97], [55, 59], [56, 58], [76, 82], [77, 81], [78, 80], [88, 92], [89, 91], [95, 93]] +(idx1, idx2) = (matched_p, matched_p) diff --git a/tests/small_samples/437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py b/tests/small_samples/437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py index d020bc8..a5f29a9 100644 --- a/tests/small_samples/437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py +++ b/tests/small_samples/437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py @@ -13,5 +13,8 @@ # # + +@foo() +@bar() class SerializedDAG: - del __get_constructor_defaults \ No newline at end of file + del __get_constructor_defaults diff --git a/tests/small_samples/4595e25f6b6dc638b75af6acd0cf094d28389c946778b9fdd85b3d392990ac1d.py b/tests/small_samples/4595e25f6b6dc638b75af6acd0cf094d28389c946778b9fdd85b3d392990ac1d.py new file mode 100644 index 0000000..72b2dd2 --- /dev/null +++ b/tests/small_samples/4595e25f6b6dc638b75af6acd0cf094d28389c946778b9fdd85b3d392990ac1d.py @@ -0,0 +1,8 @@ +def __enter__() -> 'IPCServer': + try: + pass + except WindowsError as e: + pass + else: + pass + return self \ No newline at end of file diff --git a/tests/small_samples/469d6657206073f52501ca7a3376add6c909057479278dcd6b0453bd6da0fd76.py b/tests/small_samples/469d6657206073f52501ca7a3376add6c909057479278dcd6b0453bd6da0fd76.py new file mode 100644 index 0000000..8e0075f --- /dev/null +++ b/tests/small_samples/469d6657206073f52501ca7a3376add6c909057479278dcd6b0453bd6da0fd76.py @@ -0,0 +1,6 @@ + +dict((k.lower(), v) for k, v in self.itermerged()) == dict( + + (k.lower(), v) for k, v in other.itermerged() + ) + diff --git a/tests/small_samples/49eef070677e9f94419000076b6af6c2ff3e7c004f2a0047d3c78fe16f9cf409.py b/tests/small_samples/49eef070677e9f94419000076b6af6c2ff3e7c004f2a0047d3c78fe16f9cf409.py new file mode 100644 index 0000000..b53bdff --- /dev/null +++ b/tests/small_samples/49eef070677e9f94419000076b6af6c2ff3e7c004f2a0047d3c78fe16f9cf409.py @@ -0,0 +1,7 @@ +try: + try: + pass + except () as e: + pass +finally: + pass \ No newline at end of file diff --git a/tests/small_samples/5157907c78f7584cbd8ded6a2518ef3ae01c0470af8306c9c93f2b16b277290e.py b/tests/small_samples/5157907c78f7584cbd8ded6a2518ef3ae01c0470af8306c9c93f2b16b277290e.py new file mode 100644 index 0000000..bc54bf0 --- /dev/null +++ b/tests/small_samples/5157907c78f7584cbd8ded6a2518ef3ae01c0470af8306c9c93f2b16b277290e.py @@ -0,0 +1,1595 @@ +import functools +import sys +import types +import warnings + +import unittest + +# Decorator used in the deprecation tests to reset the warning registry for +# test isolation and reproducibility. +def warningregistry(func): + def wrapper(*args, **kws): + missing = [] + saved = getattr(warnings, '__warningregistry__', missing).copy() + try: + return func(*args, **kws) + finally: + if saved is missing: + try: + del warnings.__warningregistry__ + except AttributeError: + pass + else: + warnings.__warningregistry__ = saved + return wrapper + + +class Test_TestLoader(unittest.TestCase): + + ### Basic object tests + ################################################################ + + def test___init__(self): + loader = unittest.TestLoader() + self.assertEqual([], loader.errors) + + ### Tests for TestLoader.loadTestsFromTestCase + ################################################################ + + # "Return a suite of all test cases contained in the TestCase-derived + # class testCaseClass" + def test_loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Return a suite of all test cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure it does the right thing even if no tests were found + def test_loadTestsFromTestCase__no_matches(self): + class Foo(unittest.TestCase): + def foo_bar(self): pass + + empty_suite = unittest.TestSuite() + + loader = unittest.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), empty_suite) + + # "Return a suite of all test cases contained in the TestCase-derived + # class testCaseClass" + # + # What happens if loadTestsFromTestCase() is given an object + # that isn't a subclass of TestCase? Specifically, what happens + # if testCaseClass is a subclass of TestSuite? + # + # This is checked for specifically in the code, so we better add a + # test for it. + def test_loadTestsFromTestCase__TestSuite_subclass(self): + class NotATestCase(unittest.TestSuite): + pass + + loader = unittest.TestLoader() + try: + loader.loadTestsFromTestCase(NotATestCase) + except TypeError: + pass + else: + self.fail('Should raise TypeError') + + # "Return a suite of all test cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure loadTestsFromTestCase() picks up the default test method + # name (as specified by TestCase), even though the method name does + # not match the default TestLoader.testMethodPrefix string + def test_loadTestsFromTestCase__default_method_name(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + loader = unittest.TestLoader() + # This has to be false for the test to succeed + self.assertFalse('runTest'.startswith(loader.testMethodPrefix)) + + suite = loader.loadTestsFromTestCase(Foo) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [Foo('runTest')]) + + ################################################################ + ### /Tests for TestLoader.loadTestsFromTestCase + + ### Tests for TestLoader.loadTestsFromModule + ################################################################ + + # "This method searches `module` for classes derived from TestCase" + def test_loadTestsFromModule__TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = [loader.suiteClass([MyTestCase('test')])] + self.assertEqual(list(suite), expected) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (no TestCase instances)? + def test_loadTestsFromModule__no_TestCase_instances(self): + m = types.ModuleType('m') + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (TestCases instances, but no tests)? + def test_loadTestsFromModule__no_TestCase_tests(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [loader.suiteClass()]) + + # "This method searches `module` for classes derived from TestCase"s + # + # What happens if loadTestsFromModule() is given something other + # than a module? + # + # XXX Currently, it succeeds anyway. This flexibility + # should either be documented or loadTestsFromModule() should + # raise a TypeError + # + # XXX Certain people are using this behaviour. We'll add a test for it + def test_loadTestsFromModule__not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(NotAModule) + + reference = [unittest.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + + # Check that loadTestsFromModule honors (or not) a module + # with a load_tests function. + @warningregistry + def test_loadTestsFromModule__load_tests(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, unittest.TestSuite) + self.assertEqual(load_tests_args, [loader, suite, None]) + # With Python 3.5, the undocumented and unofficial use_load_tests is + # ignored (and deprecated). + load_tests_args = [] + with warnings.catch_warnings(record=False): + warnings.simplefilter('ignore') + suite = loader.loadTestsFromModule(m, use_load_tests=False) + self.assertEqual(load_tests_args, [loader, suite, None]) + + @warningregistry + def test_loadTestsFromModule__use_load_tests_deprecated_positional(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + # The method still works. + loader = unittest.TestLoader() + # use_load_tests=True as a positional argument. + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + suite = loader.loadTestsFromModule(m, False) + self.assertIsInstance(suite, unittest.TestSuite) + # load_tests was still called because use_load_tests is deprecated + # and ignored. + self.assertEqual(load_tests_args, [loader, suite, None]) + # We got a warning. + self.assertIs(w[-1].category, DeprecationWarning) + self.assertEqual(str(w[-1].message), + 'use_load_tests is deprecated and ignored') + + @warningregistry + def test_loadTestsFromModule__use_load_tests_deprecated_keyword(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + # The method still works. + loader = unittest.TestLoader() + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + suite = loader.loadTestsFromModule(m, use_load_tests=False) + self.assertIsInstance(suite, unittest.TestSuite) + # load_tests was still called because use_load_tests is deprecated + # and ignored. + self.assertEqual(load_tests_args, [loader, suite, None]) + # We got a warning. + self.assertIs(w[-1].category, DeprecationWarning) + self.assertEqual(str(w[-1].message), + 'use_load_tests is deprecated and ignored') + + @warningregistry + def test_loadTestsFromModule__too_many_positional_args(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + loader = unittest.TestLoader() + with self.assertRaises(TypeError) as cm, \ + warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + loader.loadTestsFromModule(m, False, 'testme.*') + # We still got the deprecation warning. + self.assertIs(w[-1].category, DeprecationWarning) + self.assertEqual(str(w[-1].message), + 'use_load_tests is deprecated and ignored') + # We also got a TypeError for too many positional arguments. + self.assertEqual(type(cm.exception), TypeError) + self.assertEqual( + str(cm.exception), + 'loadTestsFromModule() takes 1 positional argument but 3 were given') + + @warningregistry + def test_loadTestsFromModule__use_load_tests_other_bad_keyword(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + loader = unittest.TestLoader() + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + with self.assertRaises(TypeError) as cm: + loader.loadTestsFromModule( + m, use_load_tests=False, very_bad=True, worse=False) + self.assertEqual(type(cm.exception), TypeError) + # The error message names the first bad argument alphabetically, + # however use_load_tests (which sorts first) is ignored. + self.assertEqual( + str(cm.exception), + "loadTestsFromModule() got an unexpected keyword argument 'very_bad'") + + def test_loadTestsFromModule__pattern(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m, pattern='testme.*') + self.assertIsInstance(suite, unittest.TestSuite) + self.assertEqual(load_tests_args, [loader, suite, 'testme.*']) + + def test_loadTestsFromModule__faulty_load_tests(self): + m = types.ModuleType('m') + + def load_tests(loader, tests, pattern): + raise TypeError('some failure') + m.load_tests = load_tests + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, unittest.TestSuite) + self.assertEqual(suite.countTestCases(), 1) + # Errors loading the suite are also captured for introspection. + self.assertNotEqual([], loader.errors) + self.assertEqual(1, len(loader.errors)) + error = loader.errors[0] + self.assertTrue( + 'Failed to call load_tests:' in error, + 'missing error string in %r' % error) + test = list(suite)[0] + + self.assertRaisesRegex(TypeError, "some failure", test.m) + + ################################################################ + ### /Tests for TestLoader.loadTestsFromModule() + + ### Tests for TestLoader.loadTestsFromName() + ################################################################ + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromName__empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('') + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the name contains invalid characters? + def test_loadTestsFromName__malformed_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('abc () //') + error, test = self.check_deferred_error(loader, suite) + expected = "Failed to import test module: abc () //" + expected_regex = r"Failed to import test module: abc \(\) //" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex( + ImportError, expected_regex, getattr(test, 'abc () //')) + + # "The specifier name is a ``dotted name'' that may resolve ... to a + # module" + # + # What happens when a module by that name can't be found? + def test_loadTestsFromName__unknown_module_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('sdasfasfasdf') + expected = "No module named 'sdasfasfasdf'" + error, test = self.check_deferred_error(loader, suite) + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module is found, but the attribute isn't? + def test_loadTestsFromName__unknown_attr_name_on_module(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('unittest.loader.sdasfasfasdf') + expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'" + error, test = self.check_deferred_error(loader, suite) + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module is found, but the attribute isn't? + def test_loadTestsFromName__unknown_attr_name_on_package(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('unittest.sdasfasfasdf') + expected = "No module named 'unittest.sdasfasfasdf'" + error, test = self.check_deferred_error(loader, suite) + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when we provide the module, but the attribute can't be + # found? + def test_loadTestsFromName__relative_unknown_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('sdasfasfasdf', unittest) + expected = "module 'unittest' has no attribute 'sdasfasfasdf'" + error, test = self.check_deferred_error(loader, suite) + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise ValueError when passed an empty + # name relative to a provided module? + # + # XXX Should probably raise a ValueError instead of an AttributeError + def test_loadTestsFromName__relative_empty_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromName('', unittest) + error, test = self.check_deferred_error(loader, suite) + expected = "has no attribute ''" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, getattr(test, '')) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when an impossible name is given, relative to the provided + # `module`? + def test_loadTestsFromName__relative_malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + suite = loader.loadTestsFromName('abc () //', unittest) + error, test = self.check_deferred_error(loader, suite) + expected = "module 'unittest' has no attribute 'abc () //'" + expected_regex = r"module 'unittest' has no attribute 'abc \(\) //'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex( + AttributeError, expected_regex, getattr(test, 'abc () //')) + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise TypeError when the `module` argument + # isn't a module object? + # + # XXX Accepts the not-a-module object, ignoring the object's type + # This should raise an exception or the method name should be changed + # + # XXX Some people are relying on this, so keep it for now + def test_loadTestsFromName__relative_not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('test_2', NotAModule) + + reference = [MyTestCase('test')] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromName__relative_bad_object(self): + m = types.ModuleType('m') + m.testcase_1 = object() + + loader = unittest.TestLoader() + try: + loader.loadTestsFromName('testcase_1', m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may + # resolve either to ... a test case class" + def test_loadTestsFromName__relative_TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testcase_1', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + def test_loadTestsFromName__relative_TestSuite(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testsuite = unittest.TestSuite([MyTestCase('test')]) + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testsuite', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test method within a test case class" + def test_loadTestsFromName__relative_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testcase_1.test', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does loadTestsFromName() raise the proper exception when trying to + # resolve "a test method within a test case class" that doesn't exist + # for the given name (relative to a provided module)? + def test_loadTestsFromName__relative_invalid_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testcase_1.testfoo', m) + expected = "type object 'MyTestCase' has no attribute 'testfoo'" + error, test = self.check_deferred_error(loader, suite) + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.testfoo) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromName__callable__TestSuite(self): + m = types.ModuleType('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + testcase_2 = unittest.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('return_TestSuite', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1, testcase_2]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromName__callable__TestCase_instance(self): + m = types.ModuleType('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('return_TestCase', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + #***************************************************************** + #Override the suiteClass attribute to ensure that the suiteClass + #attribute is used + def test_loadTestsFromName__callable__TestCase_instance_ProperSuiteClass(self): + class SubTestSuite(unittest.TestSuite): + pass + m = types.ModuleType('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest.TestLoader() + loader.suiteClass = SubTestSuite + suite = loader.loadTestsFromName('return_TestCase', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test method within a test case class" + #***************************************************************** + #Override the suiteClass attribute to ensure that the suiteClass + #attribute is used + def test_loadTestsFromName__relative_testmethod_ProperSuiteClass(self): + class SubTestSuite(unittest.TestSuite): + pass + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + loader.suiteClass=SubTestSuite + suite = loader.loadTestsFromName('testcase_1.test', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens if the callable returns something else? + def test_loadTestsFromName__callable__wrong_type(self): + m = types.ModuleType('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromName('return_wrong', m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromName__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + module_name = 'unittest.test.dummy' + sys.modules.pop(module_name, None) + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromName(module_name) + + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # module should now be loaded, thanks to loadTestsFromName() + self.assertIn(module_name, sys.modules) + finally: + if module_name in sys.modules: + del sys.modules[module_name] + + ################################################################ + ### Tests for TestLoader.loadTestsFromName() + + ### Tests for TestLoader.loadTestsFromNames() + ################################################################ + + def check_deferred_error(self, loader, suite): + """Helper function for checking that errors in loading are reported. + + :param loader: A loader with some errors. + :param suite: A suite that should have a late bound error. + :return: The first error message from the loader and the test object + from the suite. + """ + self.assertIsInstance(suite, unittest.TestSuite) + self.assertEqual(suite.countTestCases(), 1) + # Errors loading the suite are also captured for introspection. + self.assertNotEqual([], loader.errors) + self.assertEqual(1, len(loader.errors)) + error = loader.errors[0] + test = list(suite)[0] + return error, test + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # + # What happens if that sequence of names is empty? + def test_loadTestsFromNames__empty_name_list(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames([]) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens if that sequence of names is empty? + # + # XXX Should this raise a ValueError or just return an empty TestSuite? + def test_loadTestsFromNames__relative_empty_name_list(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames([], unittest) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromNames__empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['']) + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when presented with an impossible module name? + def test_loadTestsFromNames__malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise ValueError or ImportError? + suite = loader.loadTestsFromNames(['abc () //']) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "Failed to import test module: abc () //" + expected_regex = r"Failed to import test module: abc \(\) //" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex( + ImportError, expected_regex, getattr(test, 'abc () //')) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when no module can be found for the given name? + def test_loadTestsFromNames__unknown_module_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames(['sdasfasfasdf']) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "Failed to import test module: sdasfasfasdf" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module can be found, but not the attribute? + def test_loadTestsFromNames__unknown_attr_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames( + ['unittest.loader.sdasfasfasdf', 'unittest.test.dummy']) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when given an unknown attribute on a specified `module` + # argument? + def test_loadTestsFromNames__unknown_name_relative_1(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames(['sdasfasfasdf'], unittest) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "module 'unittest' has no attribute 'sdasfasfasdf'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Do unknown attributes (relative to a provided module) still raise an + # exception even in the presence of valid attribute names? + def test_loadTestsFromNames__unknown_name_relative_2(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest) + error, test = self.check_deferred_error(loader, list(suite)[1]) + expected = "module 'unittest' has no attribute 'sdasfasfasdf'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when faced with the empty string? + # + # XXX This currently raises AttributeError, though ValueError is probably + # more appropriate + def test_loadTestsFromNames__relative_empty_name(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames([''], unittest) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "has no attribute ''" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, getattr(test, '')) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when presented with an impossible attribute name? + def test_loadTestsFromNames__relative_malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + suite = loader.loadTestsFromNames(['abc () //'], unittest) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "module 'unittest' has no attribute 'abc () //'" + expected_regex = r"module 'unittest' has no attribute 'abc \(\) //'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex( + AttributeError, expected_regex, getattr(test, 'abc () //')) + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromNames() make sure the provided `module` is in fact + # a module? + # + # XXX This validation is currently not done. This flexibility should + # either be documented or a TypeError should be raised. + def test_loadTestsFromNames__relative_not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['test_2'], NotAModule) + + reference = [unittest.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromNames__relative_bad_object(self): + m = types.ModuleType('m') + m.testcase_1 = object() + + loader = unittest.TestLoader() + try: + loader.loadTestsFromNames(['testcase_1'], m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test case class" + def test_loadTestsFromNames__relative_TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1'], m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = loader.suiteClass([MyTestCase('test')]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a TestSuite instance" + def test_loadTestsFromNames__relative_TestSuite(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testsuite = unittest.TestSuite([MyTestCase('test')]) + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testsuite'], m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [m.testsuite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + def test_loadTestsFromNames__relative_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1.test'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest.TestSuite([MyTestCase('test')]) + self.assertEqual(list(suite), [ref_suite]) + + # #14971: Make sure the dotted name resolution works even if the actual + # function doesn't have the same name as is used to find it. + def test_loadTestsFromName__function_with_different_name_than_method(self): + # lambdas have the name ''. + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + test = lambda: 1 + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1.test'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest.TestSuite([MyTestCase('test')]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + # + # Does the method gracefully handle names that initially look like they + # resolve to "a test method within a test case class" but don't? + def test_loadTestsFromNames__relative_invalid_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1.testfoo'], m) + error, test = self.check_deferred_error(loader, list(suite)[0]) + expected = "type object 'MyTestCase' has no attribute 'testfoo'" + self.assertIn( + expected, error, + 'missing error string in %r' % error) + self.assertRaisesRegex(AttributeError, expected, test.testfoo) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromNames__callable__TestSuite(self): + m = types.ModuleType('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + testcase_2 = unittest.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['return_TestSuite'], m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = unittest.TestSuite([testcase_1, testcase_2]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromNames__callable__TestCase_instance(self): + m = types.ModuleType('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['return_TestCase'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # Are staticmethods handled correctly? + def test_loadTestsFromNames__callable__call_staticmethod(self): + m = types.ModuleType('m') + class Test1(unittest.TestCase): + def test(self): + pass + + testcase_1 = Test1('test') + class Foo(unittest.TestCase): + @staticmethod + def foo(): + return testcase_1 + m.Foo = Foo + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['Foo.foo'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens when the callable returns something else? + def test_loadTestsFromNames__callable__wrong_type(self): + m = types.ModuleType('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromNames(['return_wrong'], m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromNames__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + module_name = 'unittest.test.dummy' + sys.modules.pop(module_name, None) + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromNames([module_name]) + + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [unittest.TestSuite()]) + + # module should now be loaded, thanks to loadTestsFromName() + self.assertIn(module_name, sys.modules) + finally: + if module_name in sys.modules: + del sys.modules[module_name] + + ################################################################ + ### /Tests for TestLoader.loadTestsFromNames() + + ### Tests for TestLoader.getTestCaseNames() + ################################################################ + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Test.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames(self): + class Test(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + loader = unittest.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), ['test_1', 'test_2']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Does getTestCaseNames() behave appropriately if no tests are found? + def test_getTestCaseNames__no_tests(self): + class Test(unittest.TestCase): + def foobar(self): pass + + loader = unittest.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), []) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Are not-TestCases handled gracefully? + # + # XXX This should raise a TypeError, not return a list + # + # XXX It's too late in the 2.5 release cycle to fix this, but it should + # probably be revisited for 2.6 + def test_getTestCaseNames__not_a_TestCase(self): + class BadCase(int): + def test_foo(self): + pass + + loader = unittest.TestLoader() + names = loader.getTestCaseNames(BadCase) + + self.assertEqual(names, ['test_foo']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Make sure inherited names are handled. + # + # TestP.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames__inheritance(self): + class TestP(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + class TestC(TestP): + def test_1(self): pass + def test_3(self): pass + + loader = unittest.TestLoader() + + names = ['test_1', 'test_2', 'test_3'] + self.assertEqual(loader.getTestCaseNames(TestC), names) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # If TestLoader.testNamePatterns is set, only tests that match one of these + # patterns should be included. + def test_getTestCaseNames__testNamePatterns(self): + class MyTest(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + loader = unittest.TestLoader() + + loader.testNamePatterns = [] + self.assertEqual(loader.getTestCaseNames(MyTest), []) + + loader.testNamePatterns = ['*1'] + self.assertEqual(loader.getTestCaseNames(MyTest), ['test_1']) + + loader.testNamePatterns = ['*1', '*2'] + self.assertEqual(loader.getTestCaseNames(MyTest), ['test_1', 'test_2']) + + loader.testNamePatterns = ['*My*'] + self.assertEqual(loader.getTestCaseNames(MyTest), ['test_1', 'test_2']) + + loader.testNamePatterns = ['*my*'] + self.assertEqual(loader.getTestCaseNames(MyTest), []) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # If TestLoader.testNamePatterns is set, only tests that match one of these + # patterns should be included. + # + # For backwards compatibility reasons (see bpo-32071), the check may only + # touch a TestCase's attribute if it starts with the test method prefix. + def test_getTestCaseNames__testNamePatterns__attribute_access_regression(self): + class Trap: + def __get__(*ignored): + self.fail('Non-test attribute accessed') + + class MyTest(unittest.TestCase): + def test_1(self): pass + foobar = Trap() + + loader = unittest.TestLoader() + self.assertEqual(loader.getTestCaseNames(MyTest), ['test_1']) + + loader = unittest.TestLoader() + loader.testNamePatterns = [] + self.assertEqual(loader.getTestCaseNames(MyTest), []) + + ################################################################ + ### /Tests for TestLoader.getTestCaseNames() + + ### Tests for TestLoader.testMethodPrefix + ################################################################ + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests_1 = unittest.TestSuite([Foo('foo_bar')]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromModule(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = [unittest.TestSuite([Foo('foo_bar')])] + tests_2 = [unittest.TestSuite([Foo('test_1'), Foo('test_2')])] + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromName(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest.TestSuite([Foo('foo_bar')]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromNames(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest.TestSuite([unittest.TestSuite([Foo('foo_bar')])]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + tests_2 = unittest.TestSuite([tests_2]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_2) + + # "The default value is 'test'" + def test_testMethodPrefix__default_value(self): + loader = unittest.TestLoader() + self.assertEqual(loader.testMethodPrefix, 'test') + + ################################################################ + ### /Tests for TestLoader.testMethodPrefix + + ### Tests for TestLoader.sortTestMethodsUsing + ################################################################ + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromTestCase(self): + def reversed_cmp(x, y): + return -((x > y) - (x < y)) + + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromModule(self): + def reversed_cmp(x, y): + return -((x > y) - (x < y)) + + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromModule(m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromName(self): + def reversed_cmp(x, y): + return -((x > y) - (x < y)) + + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromNames(self): + def reversed_cmp(x, y): + return -((x > y) - (x < y)) + + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromNames(['Foo'], m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames()" + # + # Does it actually affect getTestCaseNames()? + def test_sortTestMethodsUsing__getTestCaseNames(self): + def reversed_cmp(x, y): + return -((x > y) - (x < y)) + + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + test_names = ['test_2', 'test_1'] + self.assertEqual(loader.getTestCaseNames(Foo), test_names) + + # "The default value is the built-in cmp() function" + # Since cmp is now defunct, we simply verify that the results + # occur in the same order as they would with the default sort. + def test_sortTestMethodsUsing__default_value(self): + loader = unittest.TestLoader() + + class Foo(unittest.TestCase): + def test_2(self): pass + def test_3(self): pass + def test_1(self): pass + + test_names = ['test_2', 'test_3', 'test_1'] + self.assertEqual(loader.getTestCaseNames(Foo), sorted(test_names)) + + + # "it can be set to None to disable the sort." + # + # XXX How is this different from reassigning cmp? Are the tests returned + # in a random order or something? This behaviour should die + def test_sortTestMethodsUsing__None(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = None + + test_names = ['test_2', 'test_1'] + self.assertEqual(set(loader.getTestCaseNames(Foo)), set(test_names)) + + ################################################################ + ### /Tests for TestLoader.sortTestMethodsUsing + + ### Tests for TestLoader.suiteClass + ################################################################ + + # "Callable object that constructs a test suite from a list of tests." + def test_suiteClass__loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromModule(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromModule(m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromName(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromNames(self): + m = types.ModuleType('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests) + + # "The default value is the TestSuite class" + def test_suiteClass__default_value(self): + loader = unittest.TestLoader() + self.assertIs(loader.suiteClass, unittest.TestSuite) + + + def test_partial_functions(self): + def noop(arg): + pass + + class Foo(unittest.TestCase): + pass + + setattr(Foo, 'test_partial', functools.partial(noop, None)) + + loader = unittest.TestLoader() + + test_names = ['test_partial'] + self.assertEqual(loader.getTestCaseNames(Foo), test_names) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/small_samples/52fcb26aa563e8a415cf74e19b2d10baca9b9c7e4e232adec6261b523fdf9ac3.py b/tests/small_samples/52fcb26aa563e8a415cf74e19b2d10baca9b9c7e4e232adec6261b523fdf9ac3.py new file mode 100644 index 0000000..a401ffe --- /dev/null +++ b/tests/small_samples/52fcb26aa563e8a415cf74e19b2d10baca9b9c7e4e232adec6261b523fdf9ac3.py @@ -0,0 +1,3 @@ +match x: + case A.y as z: + pass \ No newline at end of file diff --git a/tests/small_samples/5a0919dc81ed739ea37cb1ecc1f526c5e97da502fae63e3cd3b3ee00759a4a2e.py b/tests/small_samples/5a0919dc81ed739ea37cb1ecc1f526c5e97da502fae63e3cd3b3ee00759a4a2e.py new file mode 100644 index 0000000..7f7c2c9 --- /dev/null +++ b/tests/small_samples/5a0919dc81ed739ea37cb1ecc1f526c5e97da502fae63e3cd3b3ee00759a4a2e.py @@ -0,0 +1,11 @@ + +def http_error(status): + match status: + case 400: + return 'Bad request' + case 401 | 404: + return 'Not allowed' + case 418: + return "I'm a teapot" + + diff --git a/tests/small_samples/5b20d21dd0aa038472d978a15bd1ed58502c590664e19b020573fac98ec753b8.py b/tests/small_samples/5b20d21dd0aa038472d978a15bd1ed58502c590664e19b020573fac98ec753b8.py new file mode 100644 index 0000000..f3d2388 --- /dev/null +++ b/tests/small_samples/5b20d21dd0aa038472d978a15bd1ed58502c590664e19b020573fac98ec753b8.py @@ -0,0 +1,7 @@ +with _winreg: + for subkeyname in enum_types: + try: + with _winreg: + continue + except OSError: + pass \ No newline at end of file diff --git a/tests/small_samples/6aed1c03a7c3414b92ab41b9383f964f5c9b9af86c92289f281f95ab30acb412.py b/tests/small_samples/6aed1c03a7c3414b92ab41b9383f964f5c9b9af86c92289f281f95ab30acb412.py new file mode 100644 index 0000000..bafb8aa --- /dev/null +++ b/tests/small_samples/6aed1c03a7c3414b92ab41b9383f964f5c9b9af86c92289f281f95ab30acb412.py @@ -0,0 +1,5 @@ +def __call__(): + + async def inner(): + async with self: + return func \ No newline at end of file diff --git a/tests/small_samples/6b639409c10e0f96b497e273761b9af4b0951cbfaddf443724eafdee1572b663.py b/tests/small_samples/6b639409c10e0f96b497e273761b9af4b0951cbfaddf443724eafdee1572b663.py new file mode 100644 index 0000000..a21dcb8 --- /dev/null +++ b/tests/small_samples/6b639409c10e0f96b497e273761b9af4b0951cbfaddf443724eafdee1572b663.py @@ -0,0 +1,7 @@ + + +def f(x): + + class c(): + nonlocal x + x += 1 diff --git a/tests/small_samples/6d22e12abb1b084f0b4d96d580c7cb6fc8f032198b8b2afef18a159c7ef114fe.py b/tests/small_samples/6d22e12abb1b084f0b4d96d580c7cb6fc8f032198b8b2afef18a159c7ef114fe.py new file mode 100644 index 0000000..e06b7a3 --- /dev/null +++ b/tests/small_samples/6d22e12abb1b084f0b4d96d580c7cb6fc8f032198b8b2afef18a159c7ef114fe.py @@ -0,0 +1,2 @@ +with lock: + misses += 1 \ No newline at end of file diff --git a/tests/small_samples/6fb7148014073df0c3b2c239461678347f351b085667941524a0f807fd492803.py b/tests/small_samples/6fb7148014073df0c3b2c239461678347f351b085667941524a0f807fd492803.py new file mode 100644 index 0000000..47fb298 --- /dev/null +++ b/tests/small_samples/6fb7148014073df0c3b2c239461678347f351b085667941524a0f807fd492803.py @@ -0,0 +1,696 @@ +# Copyright The Cloud Custodian Authors. +# SPDX-License-Identifier: Apache-2.0 + +from c7n.provider import clouds, Provider + +from collections import Counter, namedtuple +import contextlib +import copy +import datetime +import itertools +import logging +import os +import operator +import sys +import time +import threading +import traceback + +import boto3 + +from botocore.validate import ParamValidator +from boto3.s3.transfer import S3Transfer + +from c7n.credentials import SessionFactory +from c7n.config import Bag +from c7n.exceptions import PolicyValidationError +from c7n.log import CloudWatchLogHandler + +from .resource_map import ResourceMap + +# Import output registries aws provider extends. +from c7n.output import ( + api_stats_outputs, + blob_outputs, + log_outputs, + metrics_outputs, + tracer_outputs +) + +# Output base implementations we extend. +from c7n.output import ( + Metrics, + DeltaStats, + BlobOutput, + LogOutput, +) + +from c7n.registry import PluginRegistry +from c7n import credentials, utils + +log = logging.getLogger('custodian.aws') + +try: + from aws_xray_sdk.core import xray_recorder, patch + from aws_xray_sdk.core.context import Context + HAVE_XRAY = True +except ImportError: + HAVE_XRAY = False + class Context: pass # NOQA + +_profile_session = None + + +DEFAULT_NAMESPACE = "CloudMaid" + + +def get_profile_session(options): + global _profile_session + if _profile_session: + return _profile_session + + profile = getattr(options, 'profile', None) + _profile_session = boto3.Session(profile_name=profile) + return _profile_session + + +def _default_region(options): + marker = object() + value = getattr(options, 'regions', marker) + if value is marker: + return + + if len(value) > 0: + return + + try: + options.regions = [get_profile_session(options).region_name] + except Exception: + log.warning('Could not determine default region') + options.regions = [None] + + if options.regions[0] is None: + log.error('No default region set. Specify a default via AWS_DEFAULT_REGION ' + 'or setting a region in ~/.aws/config') + sys.exit(1) + + log.debug("using default region:%s from boto" % options.regions[0]) + + +def _default_account_id(options): + if options.account_id: + return + elif options.assume_role: + try: + options.account_id = options.assume_role.split(':')[4] + return + except IndexError: + pass + try: + session = get_profile_session(options) + options.account_id = utils.get_account_id_from_sts(session) + except Exception: + options.account_id = None + + +def shape_validate(params, shape_name, service): + session = fake_session()._session + model = session.get_service_model(service) + shape = model.shape_for(shape_name) + validator = ParamValidator() + report = validator.validate(params, shape) + if report.has_errors(): + raise PolicyValidationError(report.generate_report()) + + +class Arn(namedtuple('_Arn', ( + 'arn', 'partition', 'service', 'region', + 'account_id', 'resource', 'resource_type', 'separator'))): + + __slots__ = () + + def __repr__(self): + return "" % ( + self.partition, + self.service, + self.region, + self.account_id, + self.resource_type, + self.separator, + self.resource) + + @classmethod + def parse(cls, arn): + if isinstance(arn, Arn): + return arn + parts = arn.split(':', 5) + # a few resources use qualifiers without specifying type + if parts[2] in ('s3', 'apigateway', 'execute-api'): + parts.append(None) + parts.append(None) + elif '/' in parts[-1]: + parts.extend(reversed(parts.pop(-1).split('/', 1))) + parts.append('/') + elif ':' in parts[-1]: + parts.extend(reversed(parts.pop(-1).split(':', 1))) + parts.append(':') + elif len(parts) == 6: + parts.append('') + parts.append('') + # replace the literal 'arn' string with raw arn + parts[0] = arn + return cls(*parts) + + +class ArnResolver: + + def __init__(self, manager): + self.manager = manager + + def resolve(self, arns): + arns = map(Arn.parse, arns) + a_service = operator.attrgetter('service') + a_resource = operator.attrgetter('resource_type') + kfunc = lambda a: (a_service(a), a_resource(a)) # noqa + arns = sorted(arns, key=kfunc) + results = {} + for (service, arn_type), arn_set in itertools.groupby(arns, key=kfunc): + arn_set = list(arn_set) + rtype = ArnResolver.resolve_type(arn_set[0]) + rmanager = self.manager.get_resource_manager(rtype) + if rtype == 'sns': + resources = rmanager.get_resources( + [rarn.arn for rarn in arn_set]) + else: + resources = rmanager.get_resources( + [rarn.resource for rarn in arn_set]) + for rarn, r in zip(rmanager.get_arns(resources), resources): + results[rarn] = r + + for rarn in arn_set: + if rarn.arn not in results: + results[rarn.arn] = None + return results + + @staticmethod + def resolve_type(arn): + arn = Arn.parse(arn) + + # this would benefit from a class cache {service} -> rtypes + for type_name, klass in AWS.resources.items(): + if type_name in ('rest-account', 'account') or klass.resource_type.arn is False: + continue + if arn.service != (klass.resource_type.arn_service or klass.resource_type.service): + continue + if (type_name in ('asg', 'ecs-task') and + "%s%s" % (klass.resource_type.arn_type, klass.resource_type.arn_separator) + in arn.resource_type): + return type_name + elif (klass.resource_type.arn_type is not None and + klass.resource_type.arn_type == arn.resource_type): + return type_name + elif (klass.resource_type.arn_service == arn.service and + klass.resource_type.arn_type == ""): + return type_name + + +@metrics_outputs.register('aws') +class MetricsOutput(Metrics): + """Send metrics data to cloudwatch + """ + + permissions = ("cloudWatch:PutMetricData",) + retry = staticmethod(utils.get_retry(('Throttling',))) + + def __init__(self, ctx, config=None): + super(MetricsOutput, self).__init__(ctx, config) + self.namespace = self.config.get('namespace', DEFAULT_NAMESPACE) + self.region = self.config.get('region') + self.destination = ( + self.config.scheme == 'aws' and + self.config.get('netloc') == 'master') and 'master' or None + + def _format_metric(self, key, value, unit, dimensions): + d = { + "MetricName": key, + "Timestamp": datetime.datetime.utcnow(), + "Value": value, + "Unit": unit} + d["Dimensions"] = [ + {"Name": "Policy", "Value": self.ctx.policy.name}, + {"Name": "ResType", "Value": self.ctx.policy.resource_type}] + for k, v in dimensions.items(): + # Skip legacy static dimensions if using new capabilities + if (self.destination or self.region) and k == 'Scope': + continue + d['Dimensions'].append({"Name": k, "Value": v}) + if self.region: + d['Dimensions'].append( + {'Name': 'Region', 'Value': self.ctx.options.region}) + if self.destination: + d['Dimensions'].append( + {'Name': 'Account', 'Value': self.ctx.options.account_id or ''}) + return d + + def _put_metrics(self, ns, metrics): + if self.destination == 'master': + watch = self.ctx.session_factory( + assume=False).client('cloudwatch', region_name=self.region) + else: + watch = utils.local_session( + self.ctx.session_factory).client('cloudwatch', region_name=self.region) + return self.retry( + watch.put_metric_data, Namespace=ns, MetricData=metrics) + + +@log_outputs.register('aws') +class CloudWatchLogOutput(LogOutput): + + log_format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s' + + def __init__(self, ctx, config=None): + super(CloudWatchLogOutput, self).__init__(ctx, config) + if self.config['netloc'] == 'master' or not self.config['netloc']: + self.log_group = self.config['path'].strip('/') + else: + # join netloc to path for casual usages of aws://log/group/name + self.log_group = ("%s/%s" % ( + self.config['netloc'], self.config['path'].strip('/'))).strip('/') + self.region = self.config.get('region', ctx.options.region) + self.destination = ( + self.config.scheme == 'aws' and + self.config.get('netloc') == 'master') and 'master' or None + + def construct_stream_name(self): + if self.config.get('stream') is None: + log_stream = self.ctx.policy.name + if self.config.get('region') is not None: + log_stream = "{}/{}".format(self.ctx.options.region, log_stream) + if self.config.get('netloc') == 'master': + log_stream = "{}/{}".format(self.ctx.options.account_id, log_stream) + else: + log_stream = self.config.get('stream').format( + region=self.ctx.options.region, + account=self.ctx.options.account_id, + policy=self.ctx.policy.name, + now=datetime.datetime.utcnow()) + return log_stream + + def get_handler(self): + log_stream = self.construct_stream_name() + params = dict( + log_group=self.log_group, log_stream=log_stream, + session_factory=( + lambda x=None: self.ctx.session_factory( + region=self.region, assume=self.destination != 'master'))) + return CloudWatchLogHandler(**params) + + def __repr__(self): + return "<%s to group:%s stream:%s>" % ( + self.__class__.__name__, + self.ctx.options.log_group, + self.ctx.policy.name) + + +class XrayEmitter: + # implement https://github.com/aws/aws-xray-sdk-python/issues/51 + + def __init__(self): + self.buf = [] + self.client = None + + def send_entity(self, entity): + self.buf.append(entity) + if len(self.buf) > 49: + self.flush() + + def flush(self): + buf = self.buf + self.buf = [] + for segment_set in utils.chunks(buf, 50): + self.client.put_trace_segments( + TraceSegmentDocuments=[s.serialize() for s in segment_set]) + + +class XrayContext(Context): + """Specialized XRay Context for Custodian. + + A context is used as a segment storage stack for currently in + progress segments. + + We use a customized context for custodian as policy execution + commonly uses a concurrent.futures threadpool pattern during + execution for api concurrency. Default xray semantics would use + thread local storage and treat each of those as separate trace + executions. We want to aggregate/associate all thread pool api + executions to the custoidan policy execution. XRay sdk supports + this via manual code for every thread pool usage, but we don't + want to explicitly couple xray integration everywhere across the + codebase. Instead we use a context that is aware of custodian + usage of threads and associates subsegments therein to the policy + execution active subsegment. + """ + + def __init__(self, *args, **kw): + super(XrayContext, self).__init__(*args, **kw) + self._local = Bag() + self._current_subsegment = None + self._main_tid = threading.get_ident() + + def handle_context_missing(self): + """Custodian has a few api calls out of band of policy execution. + + - Resolving account alias. + - Cloudwatch Log group/stream discovery/creation (when using -l on cli) + + Also we want to folks to optionally based on configuration using xray + so default to disabling context missing output. + """ + + # Annotate any segments/subsegments with their thread ids. + def put_segment(self, segment): + if getattr(segment, 'thread_id', None) is None: + segment.thread_id = threading.get_ident() + super().put_segment(segment) + + def put_subsegment(self, subsegment): + if getattr(subsegment, 'thread_id', None) is None: + subsegment.thread_id = threading.get_ident() + super().put_subsegment(subsegment) + + # Override since we're not just popping the end of the stack, we're removing + # the thread subsegment from the array by identity. + def end_subsegment(self, end_time): + subsegment = self.get_trace_entity() + if self._is_subsegment(subsegment): + subsegment.close(end_time) + self._local.entities.remove(subsegment) + return True + else: + log.warning("No subsegment to end.") + return False + + # Override get trace identity, any worker thread will find its own subsegment + # on the stack, else will use the main thread's sub/segment + def get_trace_entity(self): + tid = threading.get_ident() + entities = self._local.get('entities', ()) + for s in reversed(entities): + if s.thread_id == tid: + return s + # custodian main thread won't advance (create new segment) + # with worker threads still doing pool work. + elif s.thread_id == self._main_tid: + return s + return self.handle_context_missing() + + +@tracer_outputs.register('xray', condition=HAVE_XRAY) +class XrayTracer: + + emitter = XrayEmitter() + + in_lambda = 'LAMBDA_TASK_ROOT' in os.environ + use_daemon = 'AWS_XRAY_DAEMON_ADDRESS' in os.environ + service_name = 'custodian' + + @classmethod + def initialize(cls, config): + context = XrayContext() + sampling = config.get('sample', 'true') == 'true' and True or False + xray_recorder.configure( + emitter=cls.use_daemon is False and cls.emitter or None, + context=context, + sampling=sampling, + context_missing='LOG_ERROR') + patch(['boto3', 'requests']) + logging.getLogger('aws_xray_sdk.core').setLevel(logging.ERROR) + + def __init__(self, ctx, config): + self.ctx = ctx + self.config = config or {} + self.client = None + self.metadata = {} + + @contextlib.contextmanager + def subsegment(self, name): + segment = xray_recorder.begin_subsegment(name) + try: + yield segment + except Exception as e: + stack = traceback.extract_stack(limit=xray_recorder.max_trace_back) + segment.add_exception(e, stack) + raise + finally: + xray_recorder.end_subsegment(time.time()) + + def __enter__(self): + if self.client is None: + self.client = self.ctx.session_factory(assume=False).client('xray') + + self.emitter.client = self.client + + if self.in_lambda: + self.segment = xray_recorder.begin_subsegment(self.service_name) + else: + self.segment = xray_recorder.begin_segment( + self.service_name, sampling=True) + + p = self.ctx.policy + xray_recorder.put_annotation('policy', p.name) + xray_recorder.put_annotation('resource', p.resource_type) + if self.ctx.options.account_id: + xray_recorder.put_annotation('account', self.ctx.options.account_id) + + def __exit__(self, exc_type=None, exc_value=None, exc_traceback=None): + metadata = self.ctx.get_metadata(('api-stats',)) + metadata.update(self.metadata) + xray_recorder.put_metadata('custodian', metadata) + if self.in_lambda: + xray_recorder.end_subsegment() + return + xray_recorder.end_segment() + if not self.use_daemon: + self.emitter.flush() + log.info( + ('View XRay Trace https://console.aws.amazon.com/xray/home?region=%s#/' + 'traces/%s' % (self.ctx.options.region, self.segment.trace_id))) + self.metadata.clear() + + +@api_stats_outputs.register('aws') +class ApiStats(DeltaStats): + + def __init__(self, ctx, config=None): + super(ApiStats, self).__init__(ctx, config) + self.api_calls = Counter() + + def get_snapshot(self): + return dict(self.api_calls) + + def get_metadata(self): + return self.get_snapshot() + + def __enter__(self): + if isinstance(self.ctx.session_factory, credentials.SessionFactory): + self.ctx.session_factory.set_subscribers((self,)) + self.push_snapshot() + + def __exit__(self, exc_type=None, exc_value=None, exc_traceback=None): + if isinstance(self.ctx.session_factory, credentials.SessionFactory): + self.ctx.session_factory.set_subscribers(()) + + # With cached sessions, we need to unregister any events subscribers + # on extant sessions to allow for the next registration. + utils.local_session(self.ctx.session_factory).events.unregister( + 'after-call.*.*', self._record, unique_id='c7n-api-stats') + + self.ctx.metrics.put_metric( + "ApiCalls", sum(self.api_calls.values()), "Count") + self.pop_snapshot() + + def __call__(self, s): + s.events.register( + 'after-call.*.*', self._record, unique_id='c7n-api-stats') + + def _record(self, http_response, parsed, model, **kwargs): + self.api_calls["%s.%s" % ( + model.service_model.endpoint_prefix, model.name)] += 1 + + +@blob_outputs.register('s3') +class S3Output(BlobOutput): + """ + Usage: + + .. code-block:: python + + with S3Output(session_factory, 's3://bucket/prefix'): + log.info('xyz') # -> log messages sent to custodian-run.log.gz + + """ + + permissions = ('S3:PutObject',) + + def __init__(self, ctx, config): + super().__init__(ctx, config) + # can't use a local session as we dont want an unassumed session cached. + s3_client = self.ctx.session_factory(assume=False).client('s3') + region = s3_client.get_bucket_location(Bucket=self.bucket)['LocationConstraint'] + # if region is None, we use us-east-1 + region = region or "us-east-1" + self.transfer = S3Transfer( + self.ctx.session_factory(region=region, assume=False).client('s3')) + + def upload_file(self, path, key): + self.transfer.upload_file( + path, self.bucket, key, + extra_args={ + 'ACL': 'bucket-owner-full-control', + 'ServerSideEncryption': 'AES256'}) + + +@clouds.register('aws') +class AWS(Provider): + + display_name = 'AWS' + resource_prefix = 'aws' + # legacy path for older plugins + resources = PluginRegistry('resources') + # import paths for resources + resource_map = ResourceMap + + def initialize(self, options): + """ + """ + _default_region(options) + _default_account_id(options) + if options.tracer and options.tracer.startswith('xray') and HAVE_XRAY: + XrayTracer.initialize(utils.parse_url_config(options.tracer)) + + return options + + def get_session_factory(self, options): + return SessionFactory( + options.region, + options.profile, + options.assume_role, + options.external_id) + + def initialize_policies(self, policy_collection, options): + """Return a set of policies targetted to the given regions. + + Supports symbolic regions like 'all'. This will automatically + filter out policies if they are being targetted to a region that + does not support the service. Global services will target a + single region (us-east-1 if only all specified, else first + region in the list). + + Note for region partitions (govcloud and china) an explicit + region from the partition must be passed in. + """ + from c7n.policy import Policy, PolicyCollection + policies = [] + service_region_map, resource_service_map = get_service_region_map( + options.regions, policy_collection.resource_types, self.type) + if 'all' in options.regions: + enabled_regions = { + r['RegionName'] for r in + get_profile_session(options).client('ec2').describe_regions( + Filters=[{'Name': 'opt-in-status', + 'Values': ['opt-in-not-required', 'opted-in']}] + ).get('Regions')} + for p in policy_collection: + if 'aws.' in p.resource_type: + _, resource_type = p.resource_type.split('.', 1) + else: + resource_type = p.resource_type + available_regions = service_region_map.get( + resource_service_map.get(resource_type), ()) + + # its a global service/endpoint, use user provided region + # or us-east-1. + if not available_regions and options.regions: + candidates = [r for r in options.regions if r != 'all'] + candidate = candidates and candidates[0] or 'us-east-1' + svc_regions = [candidate] + elif 'all' in options.regions: + svc_regions = list(set(available_regions).intersection(enabled_regions)) + else: + svc_regions = options.regions + + for region in svc_regions: + if available_regions and region not in available_regions: + level = ('all' in options.regions and + logging.DEBUG or logging.WARNING) + # TODO: fixme + policy_collection.log.log( + level, "policy:%s resources:%s not available in region:%s", + p.name, p.resource_type, region) + continue + options_copy = copy.copy(options) + options_copy.region = str(region) + + if len(options.regions) > 1 or 'all' in options.regions and getattr( + options, 'output_dir', None): + options_copy.output_dir = join_output(options.output_dir, region) + policies.append( + Policy(p.data, options_copy, + session_factory=policy_collection.session_factory())) + + return PolicyCollection( + # order policies by region to minimize local session invalidation. + # note relative ordering of policies must be preserved, python sort + # is stable. + sorted(policies, key=operator.attrgetter('options.region')), + options) + + +def join_output(output_dir, suffix): + if '{region}' in output_dir: + return output_dir.rstrip('/') + if output_dir.endswith('://'): + return output_dir + suffix + return output_dir.rstrip('/') + '/%s' % suffix + + +def fake_session(): + session = boto3.Session( # nosec nosemgrep + region_name='us-east-1', + aws_access_key_id='never', + aws_secret_access_key='found') + return session + + +def get_service_region_map(regions, resource_types, provider='aws'): + # we're not interacting with the apis just using the sdk meta information. + + session = fake_session() + normalized_types = [] + for r in resource_types: + if r.startswith('%s.' % provider): + normalized_types.append(r[len(provider) + 1:]) + else: + normalized_types.append(r) + + resource_service_map = { + r: clouds[provider].resources.get(r).resource_type.service + for r in normalized_types if r != 'account'} + # support for govcloud and china, we only utilize these regions if they + # are explicitly passed in on the cli. + partition_regions = {} + for p in ('aws-cn', 'aws-us-gov'): + for r in session.get_available_regions('s3', partition_name=p): + partition_regions[r] = p + + partitions = ['aws'] + for r in regions: + if r in partition_regions: + partitions.append(partition_regions[r]) + + service_region_map = {} + for s in set(itertools.chain(resource_service_map.values())): + for partition in partitions: + service_region_map.setdefault(s, []).extend( + session.get_available_regions(s, partition_name=partition)) + return service_region_map, resource_service_map diff --git a/tests/small_samples/7156f124f7d8eeda4ad376f14982771a1ccedfebdd2bec7361fe3b10ee47ce4a.py b/tests/small_samples/7156f124f7d8eeda4ad376f14982771a1ccedfebdd2bec7361fe3b10ee47ce4a.py new file mode 100644 index 0000000..a61e1a4 --- /dev/null +++ b/tests/small_samples/7156f124f7d8eeda4ad376f14982771a1ccedfebdd2bec7361fe3b10ee47ce4a.py @@ -0,0 +1,44 @@ +if isinstance(file, int): + os.fspath(file) +if isinstance(file, (str, bytes, int)): + TypeError('invalid file: %r' % file) +if isinstance(mode, str): + TypeError('invalid mode: %r' % mode) +if isinstance(buffering, int): + TypeError('invalid buffering: %r' % buffering) +if encoding is not None and isinstance(encoding, str): + TypeError('invalid encoding: %r' % encoding) +if errors is not None and isinstance(errors, str): + TypeError('invalid errors: %r' % errors) +set(mode) +if modes - set('axrwb+tU') or len(mode) > len(modes): + ValueError('invalid mode: %r' % mode) +'x' in modes +'r' in modes +'w' in modes +'a' in modes +'+' in modes +'t' in modes +'b' in modes +if 'U' in modes: + if creating or writing or appending or updating: + ValueError("mode U cannot be combined with 'x', 'w', 'a', or '+'") + import warnings + warnings.warn("'U' mode is deprecated", DeprecationWarning, 2) + reading = True +if text and binary: + ValueError("can't have text and binary mode at once") +if creating + reading + writing + appending > 1: + ValueError("can't have read/write/append mode at once") +if creating or reading or writing or appending: + ValueError('must have exactly one of read/write/append mode') +if binary and encoding is not None: + ValueError("binary mode doesn't take an encoding argument") +if binary and errors is not None: + ValueError("binary mode doesn't take an errors argument") +if binary and newline is not None: + ValueError("binary mode doesn't take a newline argument") +if binary and buffering == 1: + import warnings + warnings.warn("line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used", RuntimeWarning, 2) +(creating) + (reading and 'r' or '') + (writing and 'w' or '') + (appending and 'a' or '') diff --git a/tests/small_samples/721393430663221b125b44a26d99e7890cc2986944ae56d4b3f130e459e0a9ea.py b/tests/small_samples/721393430663221b125b44a26d99e7890cc2986944ae56d4b3f130e459e0a9ea.py new file mode 100644 index 0000000..fee0102 --- /dev/null +++ b/tests/small_samples/721393430663221b125b44a26d99e7890cc2986944ae56d4b3f130e459e0a9ea.py @@ -0,0 +1,2 @@ +def __init__(): + db_uri or os \ No newline at end of file diff --git a/tests/small_samples/7f066665ce508a39919d6a1b983171180b86e25a639079e64dcf9672c2cc2abe.py b/tests/small_samples/7f066665ce508a39919d6a1b983171180b86e25a639079e64dcf9672c2cc2abe.py new file mode 100644 index 0000000..1a8216e --- /dev/null +++ b/tests/small_samples/7f066665ce508a39919d6a1b983171180b86e25a639079e64dcf9672c2cc2abe.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import +'spam, bar, blah' +from __future__ import print_function \ No newline at end of file diff --git a/tests/small_samples/7f9ef841e54b680b479c91f764049ae8ca04539f9b9484af307c978d9155df4b.py b/tests/small_samples/7f9ef841e54b680b479c91f764049ae8ca04539f9b9484af307c978d9155df4b.py new file mode 100644 index 0000000..1868672 --- /dev/null +++ b/tests/small_samples/7f9ef841e54b680b479c91f764049ae8ca04539f9b9484af307c978d9155df4b.py @@ -0,0 +1 @@ +assert len == 2, 'Watch extra must return pair or None' \ No newline at end of file diff --git a/tests/small_samples/883be65bcbf235e49778899bb29c8ab58bd75fcf68afcad63c8a116a48254e3d.py b/tests/small_samples/883be65bcbf235e49778899bb29c8ab58bd75fcf68afcad63c8a116a48254e3d.py new file mode 100644 index 0000000..f75e662 --- /dev/null +++ b/tests/small_samples/883be65bcbf235e49778899bb29c8ab58bd75fcf68afcad63c8a116a48254e3d.py @@ -0,0 +1,3 @@ +for ss_name in itertools: + continue + [os for fname in manual_dl_files] \ No newline at end of file diff --git a/tests/small_samples/8aacbb80e3b6e370d46540c82d9dcce1e2b361277fc4d8b1d7f0cacaab6d7405.py b/tests/small_samples/8aacbb80e3b6e370d46540c82d9dcce1e2b361277fc4d8b1d7f0cacaab6d7405.py new file mode 100644 index 0000000..197b099 --- /dev/null +++ b/tests/small_samples/8aacbb80e3b6e370d46540c82d9dcce1e2b361277fc4d8b1d7f0cacaab6d7405.py @@ -0,0 +1,4 @@ +def test_patma_008(): + match x: + case A.y as z: + pass \ No newline at end of file diff --git a/tests/small_samples/8c13edc556b830801af8780d903e3c8b8d13f643e05dc3879e934f72ff573258.py b/tests/small_samples/8c13edc556b830801af8780d903e3c8b8d13f643e05dc3879e934f72ff573258.py new file mode 100644 index 0000000..2fae253 --- /dev/null +++ b/tests/small_samples/8c13edc556b830801af8780d903e3c8b8d13f643e05dc3879e934f72ff573258.py @@ -0,0 +1,10 @@ +def read() -> bytes: + if sys: + while True: + if err: + break + else: + pass + else: + pass + return bytes(bdata) \ No newline at end of file diff --git a/tests/small_samples/8fe94ee74887b182542b2781c81e4bbb90e9db6e923f453f53f12dd33bf35c70.py b/tests/small_samples/8fe94ee74887b182542b2781c81e4bbb90e9db6e923f453f53f12dd33bf35c70.py new file mode 100644 index 0000000..5c3a773 --- /dev/null +++ b/tests/small_samples/8fe94ee74887b182542b2781c81e4bbb90e9db6e923f453f53f12dd33bf35c70.py @@ -0,0 +1,7 @@ +def test_nested_names(): + global Nested + + class Nested: + + class C: + pass \ No newline at end of file diff --git a/tests/small_samples/900f43a68dee367f6a979faac72bc2da2e3f3f273eb879afe8741f56d41a77cb.py b/tests/small_samples/900f43a68dee367f6a979faac72bc2da2e3f3f273eb879afe8741f56d41a77cb.py new file mode 100644 index 0000000..66b4282 --- /dev/null +++ b/tests/small_samples/900f43a68dee367f6a979faac72bc2da2e3f3f273eb879afe8741f56d41a77cb.py @@ -0,0 +1,4 @@ +[[1.0, 49], [1, 41], [1, 40], [1, 25], [1, 21], [1, 21], [1, 19], [1, 19], [1, 18], [1, 18], [1, 16], [1, 15], [1, 15], [1, 15], [1, 15], [1, 14], [1, 14], [1, 13], [1, 13], [1, 13], [1, 13], [1, 12], [1, 12], [1, 11], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 10], [1, 9], [1, 9], [1, 9]] + +def least_squares_fit(xs: List[Vector]=0.001, ys: List[float]=1000) -> Vector: + pass \ No newline at end of file diff --git a/tests/small_samples/905970cd22404352cb8285d588fd3a70c5803608ed0d0b9bfb38c1470899f0cc.py b/tests/small_samples/905970cd22404352cb8285d588fd3a70c5803608ed0d0b9bfb38c1470899f0cc.py new file mode 100644 index 0000000..207c6ca --- /dev/null +++ b/tests/small_samples/905970cd22404352cb8285d588fd3a70c5803608ed0d0b9bfb38c1470899f0cc.py @@ -0,0 +1,3 @@ +match (0, 1, 2): + case [*x]: + y = 0 \ No newline at end of file diff --git a/tests/small_samples/93bfda368bdc4523c8832de1f799d9074bd0c2a3a899da14bd897be4e0dc6f69.py b/tests/small_samples/93bfda368bdc4523c8832de1f799d9074bd0c2a3a899da14bd897be4e0dc6f69.py new file mode 100644 index 0000000..6fbeb1e --- /dev/null +++ b/tests/small_samples/93bfda368bdc4523c8832de1f799d9074bd0c2a3a899da14bd897be4e0dc6f69.py @@ -0,0 +1 @@ +(isinstance and parent or isinstance) \ No newline at end of file diff --git a/tests/small_samples/99b33978257ea7093798a8b75a6999841b5c54875f08368da8a36a763a426c5b.py b/tests/small_samples/99b33978257ea7093798a8b75a6999841b5c54875f08368da8a36a763a426c5b.py new file mode 100644 index 0000000..949917b --- /dev/null +++ b/tests/small_samples/99b33978257ea7093798a8b75a6999841b5c54875f08368da8a36a763a426c5b.py @@ -0,0 +1,3 @@ +async def test_enter(): + async with manager as context: + self \ No newline at end of file diff --git a/tests/small_samples/a14d70a56f75fb988c9c7dc5b93fb0a89725d076ec981846ecda55f56ac36dc7.py b/tests/small_samples/a14d70a56f75fb988c9c7dc5b93fb0a89725d076ec981846ecda55f56ac36dc7.py new file mode 100644 index 0000000..9d65bd8 --- /dev/null +++ b/tests/small_samples/a14d70a56f75fb988c9c7dc5b93fb0a89725d076ec981846ecda55f56ac36dc7.py @@ -0,0 +1,3 @@ +match (0, 1, 2): + case [0, *x]: + y = 0 \ No newline at end of file diff --git a/tests/small_samples/a6f1ed90d0f5ff987919ff611aab7332d8ab2011fe72bb822ef058b1b8a24ef7.py b/tests/small_samples/a6f1ed90d0f5ff987919ff611aab7332d8ab2011fe72bb822ef058b1b8a24ef7.py new file mode 100644 index 0000000..edb0e55 --- /dev/null +++ b/tests/small_samples/a6f1ed90d0f5ff987919ff611aab7332d8ab2011fe72bb822ef058b1b8a24ef7.py @@ -0,0 +1 @@ +assert rawdata == ' 19): + SYMBOL_TITLEBAR_MAXIMIZE = '◻' + SYMBOL_TITLEBAR_CLOSE = 'X' +else: + SYMBOL_TITLEBAR_MINIMIZE = '_' + SYMBOL_TITLEBAR_MAXIMIZE = 'O' + SYMBOL_TITLEBAR_CLOSE = 'X' +DEFAULT_USER_SETTINGS_WIN_PATH = '~\\AppData\\Local\\PySimpleGUI\\settings' +DEFAULT_USER_SETTINGS_LINUX_PATH = '~/.config/PySimpleGUI/settings' +DEFAULT_USER_SETTINGS_MAC_PATH = '~/Library/Application Support/PySimpleGUI/settings' +DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME = '_PySimpleGUI_settings_global_.json' + +def rgb(): + pass +BUTTON_TYPE_BROWSE_FILES = 21 +BUTTON_TYPE_CLOSES_WIN = 5 +BUTTON_TYPE_CLOSES_WIN_ONLY = 6 +BUTTON_TYPE_READ_FORM = 7 +BUTTON_TYPE_REALTIME = 9 +BUTTON_TYPE_CALENDAR_CHOOSER = 30 +BUTTON_TYPE_COLOR_CHOOSER = 40 +BROWSE_FILES_DELIMITER = ';' +FILE_TYPES_ALL_FILES = () +BUTTON_DISABLED_MEANS_IGNORE = 'ignore' +ELEM_TYPE_TEXT = 'text' +ELEM_TYPE_INPUT_TEXT = 'input' +ELEM_TYPE_INPUT_COMBO = 'combo' +ELEM_TYPE_INPUT_OPTION_MENU = 'option menu' +ELEM_TYPE_INPUT_RADIO = 'radio' +ELEM_TYPE_INPUT_MULTILINE = 'multiline' +ELEM_TYPE_INPUT_CHECKBOX = 'checkbox' +ELEM_TYPE_INPUT_SPIN = 'spind' +ELEM_TYPE_BUTTON = 'button' +ELEM_TYPE_IMAGE = 'image' +ELEM_TYPE_CANVAS = 'canvas' +ELEM_TYPE_FRAME = 'frame' +ELEM_TYPE_GRAPH = 'graph' +ELEM_TYPE_TAB = 'tab' +ELEM_TYPE_TAB_GROUP = 'tabgroup' +ELEM_TYPE_INPUT_SLIDER = 'slider' +ELEM_TYPE_INPUT_LISTBOX = 'listbox' +ELEM_TYPE_OUTPUT = 'output' +ELEM_TYPE_COLUMN = 'column' +ELEM_TYPE_MENUBAR = 'menubar' +ELEM_TYPE_PROGRESS_BAR = 'progressbar' +ELEM_TYPE_BLANK = 'blank' +ELEM_TYPE_TABLE = 'table' +ELEM_TYPE_TREE = 'tree' +ELEM_TYPE_ERROR = 'error' +ELEM_TYPE_SEPARATOR = 'separator' +ELEM_TYPE_STATUSBAR = 'statusbar' +ELEM_TYPE_PANE = 'pane' +ELEM_TYPE_BUTTONMENU = 'buttonmenu' +ELEM_TYPE_TITLEBAR = 'titlebar' +ELEM_TYPE_SIZEGRIP = 'sizegrip' +POPUP_BUTTONS_OK_CANCEL = 4 +PSG_THEME_PART_BUTTON_TEXT = 'Button Text Color' +PSG_THEME_PART_BUTTON_BACKGROUND = 'Button Background Color' +PSG_THEME_PART_BACKGROUND = 'Background Color' +PSG_THEME_PART_INPUT_BACKGROUND = 'Input Element Background Color' +PSG_THEME_PART_INPUT_TEXT = 'Input Element Text Color' +PSG_THEME_PART_TEXT = 'Text Color' +PSG_THEME_PART_SLIDER = 'Slider Color' +TTK_SCROLLBAR_PART_TROUGH_COLOR = 'Trough Color' +TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR = 'Arrow Button Arrow Color' +TTK_SCROLLBAR_PART_FRAME_COLOR = 'Frame Color' +TTK_SCROLLBAR_PART_SCROLL_WIDTH = 'Frame Width' +TTK_SCROLLBAR_PART_ARROW_WIDTH = 'Arrow Width' +TTK_SCROLLBAR_PART_RELIEF = 'Relief' +DEFAULT_TTK_PART_MAPPING_DICT = {TTK_SCROLLBAR_PART_TROUGH_COLOR: PSG_THEME_PART_SLIDER, TTK_SCROLLBAR_PART_BACKGROUND_COLOR: PSG_THEME_PART_BUTTON_BACKGROUND, TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR: PSG_THEME_PART_BUTTON_TEXT, TTK_SCROLLBAR_PART_FRAME_COLOR: PSG_THEME_PART_BACKGROUND, TTK_SCROLLBAR_PART_SCROLL_WIDTH: 12, TTK_SCROLLBAR_PART_ARROW_WIDTH: 12, TTK_SCROLLBAR_PART_RELIEF: RELIEF_RAISED} + +def __init__(): + pass +TKINTER_CURSORS = ['X_cursor', 'arrow', 'based_arrow_down', 'based_arrow_up', 'boat', 'bogosity', 'bottom_left_corner', 'bottom_right_corner', 'bottom_side', 'bottom_tee', 'box_spiral', 'center_ptr', 'circle', 'clock', 'coffee_mug', 'cross', 'cross_reverse', 'crosshair', 'diamond_cross', 'dot', 'dotbox', 'double_arrow', 'draft_large', 'draft_small', 'draped_box', 'exchange', 'fleur', 'gobbler', 'gumby', 'hand1', 'hand2', 'heart', 'icon', 'iron_cross', 'left_ptr', 'left_side', 'left_tee', 'leftbutton', 'll_angle', 'lr_angle', 'man', 'middlebutton', 'mouse', 'pencil', 'pirate', 'plus', 'question_arrow', 'right_ptr', 'right_side', 'right_tee', 'rightbutton', 'rtl_logo', 'sailboat', 'sb_down_arrow', 'sb_h_double_arrow', 'sb_left_arrow', 'sb_right_arrow', 'sb_up_arrow', 'sb_v_double_arrow', 'shuttle', 'sizing', 'spider', 'spraycan', 'star', 'target', 'tcross', 'top_left_arrow', 'top_left_corner', 'top_right_corner', 'top_side', 'top_tee', 'trek', 'ul_angle', 'umbrella', 'ur_angle', 'watch', 'xterm'] + +def popup_get_date(start_mon=None, start_day=None, start_year=None, begin_at_sunday_plus=0, no_titlebar=True, title='Choose Date'): + pass diff --git a/tests/small_samples/abdc819dd287ed2f393276c033489c7dcc564226eaabae99e53aed7e4c155f92.py b/tests/small_samples/abdc819dd287ed2f393276c033489c7dcc564226eaabae99e53aed7e4c155f92.py new file mode 100644 index 0000000..57af934 --- /dev/null +++ b/tests/small_samples/abdc819dd287ed2f393276c033489c7dcc564226eaabae99e53aed7e4c155f92.py @@ -0,0 +1 @@ +(a for a in attrs if a or (a and a is True)) \ No newline at end of file diff --git a/tests/small_samples/abfd4e130cff63cd19d7fefd09e99668adf43975c9ae2b3912b3bfa2de00847b.py b/tests/small_samples/abfd4e130cff63cd19d7fefd09e99668adf43975c9ae2b3912b3bfa2de00847b.py new file mode 100644 index 0000000..52bc8e7 --- /dev/null +++ b/tests/small_samples/abfd4e130cff63cd19d7fefd09e99668adf43975c9ae2b3912b3bfa2de00847b.py @@ -0,0 +1,5 @@ +def __init__(): + pass +for model in []: + if self: + pass diff --git a/tests/small_samples/acc1d8132f178c485aa2238b87ad8831b1c4b3cd3a4189ae3e82399edfb45903.py b/tests/small_samples/acc1d8132f178c485aa2238b87ad8831b1c4b3cd3a4189ae3e82399edfb45903.py new file mode 100644 index 0000000..3b79554 --- /dev/null +++ b/tests/small_samples/acc1d8132f178c485aa2238b87ad8831b1c4b3cd3a4189ae3e82399edfb45903.py @@ -0,0 +1,2 @@ +with self: + self([len(x) for x in obj], [len(x) for x in unpickled]) \ No newline at end of file diff --git a/tests/small_samples/b53c9994dfb88cf3ecc11886cbdc81535b054b4cc413bed0cd3d77a501bd3e37.py b/tests/small_samples/b53c9994dfb88cf3ecc11886cbdc81535b054b4cc413bed0cd3d77a501bd3e37.py new file mode 100644 index 0000000..7e3c00b --- /dev/null +++ b/tests/small_samples/b53c9994dfb88cf3ecc11886cbdc81535b054b4cc413bed0cd3d77a501bd3e37.py @@ -0,0 +1,2 @@ +while True: + (isinstance and parent) \ No newline at end of file diff --git a/tests/small_samples/b576160fb165e2bd7df3c433102a771eaefd0a54ee27b25ad32ddd5e13592893.py b/tests/small_samples/b576160fb165e2bd7df3c433102a771eaefd0a54ee27b25ad32ddd5e13592893.py new file mode 100644 index 0000000..fe51301 --- /dev/null +++ b/tests/small_samples/b576160fb165e2bd7df3c433102a771eaefd0a54ee27b25ad32ddd5e13592893.py @@ -0,0 +1 @@ +(isinstance or isinstance) and node \ No newline at end of file diff --git a/tests/small_samples/b7925c6615e1773292084c72800ec41de7e4136058575e03cee0399b596ffe85.py b/tests/small_samples/b7925c6615e1773292084c72800ec41de7e4136058575e03cee0399b596ffe85.py new file mode 100644 index 0000000..3cd89b2 --- /dev/null +++ b/tests/small_samples/b7925c6615e1773292084c72800ec41de7e4136058575e03cee0399b596ffe85.py @@ -0,0 +1,3 @@ +while True: + if s or s: + break \ No newline at end of file diff --git a/tests/small_samples/bcf3f4ff8ace3d3f9d6e9a216ef89d6d36883da620cac3860e8ef7a1f3ed92b0.py b/tests/small_samples/bcf3f4ff8ace3d3f9d6e9a216ef89d6d36883da620cac3860e8ef7a1f3ed92b0.py new file mode 100644 index 0000000..73b3bc2 --- /dev/null +++ b/tests/small_samples/bcf3f4ff8ace3d3f9d6e9a216ef89d6d36883da620cac3860e8ef7a1f3ed92b0.py @@ -0,0 +1,7 @@ +def _get_expected_error_settings_dict(): + try: + pass + except Exception as e: + pass + _EXPECTED_ERROR_SETTINGS_DICT = expected_error_settings_dict + return _EXPECTED_ERROR_SETTINGS_DICT \ No newline at end of file diff --git a/tests/small_samples/cbdec30fc69f8d42b2e6a025f55ba485736d84287da9cd35a919cdf25662873b.py b/tests/small_samples/cbdec30fc69f8d42b2e6a025f55ba485736d84287da9cd35a919cdf25662873b.py new file mode 100644 index 0000000..eaceb47 --- /dev/null +++ b/tests/small_samples/cbdec30fc69f8d42b2e6a025f55ba485736d84287da9cd35a919cdf25662873b.py @@ -0,0 +1 @@ +f"\n (?:\n # an empty statement\n (?: # \n ;\n )\n |\n\n # compound type decl (maybe inline)\n (?:\n (?:\n (?: # \n [^;{{}}]+?\n )\n \\s*\n )?\n (?: # \n {COMPOUND_TYPE_KIND}\n )\n (?:\n \\s+\n (?: # \n {STRICT_IDENTIFIER}\n )\n )?\n \\s* {{\n )\n |\n # bogus inline decl artifact\n # This simplifies resolving the relative syntactic ambiguity of\n # inline structs.\n (?:\n (?: # \n {COMPOUND_TYPE_KIND}\n )\n \\s*\n (?: # \n {ANON_IDENTIFIER}\n )\n (?: # \n [^=,;({{[*\\]]*\n [=,;({{]\n )\n )\n |\n\n # typedef\n (?:\n \\b typedef \\b \\s*\n (?: # \n {_ind}\n )\n (?:\n # We expect no inline type definitions in the parameters.\n \\s* [(] \\s*\n (?: # \n [^{{;]*\n )\n \\s* [)]\n )?\n \\s* ;\n )\n |\n\n # func decl/definition & var decls\n # XXX dedicated pattern for funcs (more restricted)?\n (?:\n (?:\n (?: # \n {STORAGE_CLASS}\n )\n \\s*\n )?\n (?:\n (?: # \n \\b inline \\b\n )\n \\s*\n )?\n (?: # \n {_ind}\n )\n (?:\n # func decl / definition\n (?:\n (?:\n # We expect no inline type definitions in the parameters.\n \\s* [(] \\s*\n (?: # \n [^{{;]*\n )\n \\s* [)] \\s*\n (?: # \n [{{;]\n )\n )\n |\n (?:\n # This is some old-school syntax!\n \\s* [(] \\s*\n # We throw away the bare names:\n {STRICT_IDENTIFIER}\n (?: \\s* , \\s* {STRICT_IDENTIFIER} )*\n \\s* [)] \\s*\n\n # We keep the trailing param declarations:\n (?: # \n # There's at least one!\n (?: {TYPE_QUALIFIER} \\s* )?\n {_ind}\n \\s*\n {_ind}\n \\s* ;\n (?:\n \\s*\n (?: {TYPE_QUALIFIER} \\s* )?\n {_ind}\n \\s*\n {_ind}\n \\s* ;\n )*\n )\n \\s* {{\n )\n )\n |\n # var / typedef\n (?:\n (?:\n # initializer\n # We expect only basic initializers.\n \\s* = \\s*\n (?: # \n " \ No newline at end of file diff --git a/tests/small_samples/d17071caf073b1360d7ceca7507ae9ba19c67380b90171cd5e7a4d3c7d0c601d.py b/tests/small_samples/d17071caf073b1360d7ceca7507ae9ba19c67380b90171cd5e7a4d3c7d0c601d.py new file mode 100644 index 0000000..c5d4b7e --- /dev/null +++ b/tests/small_samples/d17071caf073b1360d7ceca7507ae9ba19c67380b90171cd5e7a4d3c7d0c601d.py @@ -0,0 +1,10 @@ +if sys: + try: + pass + except WindowsError as e: + pass + else: + pass +else: + pass +self \ No newline at end of file diff --git a/tests/small_samples/d368fd6732cfc910e0b575fcbc46f231d43e5823bdb830d2144a4c22b6212196.py b/tests/small_samples/d368fd6732cfc910e0b575fcbc46f231d43e5823bdb830d2144a4c22b6212196.py new file mode 100644 index 0000000..9c11cea --- /dev/null +++ b/tests/small_samples/d368fd6732cfc910e0b575fcbc46f231d43e5823bdb830d2144a4c22b6212196.py @@ -0,0 +1,7 @@ +with patch: + + def outer(): + + @cache_decorator + def inner(): + pass \ No newline at end of file diff --git a/tests/small_samples/e3e840142bcaafd6a91eb2625e7a250a4a2a8e96f64c083e0839bf649953ce66.py b/tests/small_samples/e3e840142bcaafd6a91eb2625e7a250a4a2a8e96f64c083e0839bf649953ce66.py new file mode 100644 index 0000000..aabf448 --- /dev/null +++ b/tests/small_samples/e3e840142bcaafd6a91eb2625e7a250a4a2a8e96f64c083e0839bf649953ce66.py @@ -0,0 +1 @@ +path if '__DECODE_PATH__' else path \ No newline at end of file diff --git a/tests/small_samples/e6e866e6114d5c9fad9804cdaee28d172c78385c7640ddbb2a486ecefb21387b.py b/tests/small_samples/e6e866e6114d5c9fad9804cdaee28d172c78385c7640ddbb2a486ecefb21387b.py new file mode 100644 index 0000000..2866d69 --- /dev/null +++ b/tests/small_samples/e6e866e6114d5c9fad9804cdaee28d172c78385c7640ddbb2a486ecefb21387b.py @@ -0,0 +1,2 @@ +raise NotImplementedError +(d for d in decls) \ No newline at end of file diff --git a/tests/small_samples/eca1e5b5d7d615d7bbd1544076a6b1069e4509dd6b30b3500b323ad6f4517244.py b/tests/small_samples/eca1e5b5d7d615d7bbd1544076a6b1069e4509dd6b30b3500b323ad6f4517244.py new file mode 100644 index 0000000..58c3ef9 --- /dev/null +++ b/tests/small_samples/eca1e5b5d7d615d7bbd1544076a6b1069e4509dd6b30b3500b323ad6f4517244.py @@ -0,0 +1 @@ +(x and fmt) and fmt \ No newline at end of file diff --git a/tests/small_samples/ee0b80f13f1d4195efba333ee063cf7c352d4b52152ebc8582e2508319de8a6e.py b/tests/small_samples/ee0b80f13f1d4195efba333ee063cf7c352d4b52152ebc8582e2508319de8a6e.py new file mode 100644 index 0000000..fdc51fa --- /dev/null +++ b/tests/small_samples/ee0b80f13f1d4195efba333ee063cf7c352d4b52152ebc8582e2508319de8a6e.py @@ -0,0 +1,3 @@ +def __init__(): + self = db_uri or os or b + q diff --git a/tests/small_samples/f09192915e250c0e1630b5d9add1328874fcb799cc508db1d7b6a880b2d0acea.py b/tests/small_samples/f09192915e250c0e1630b5d9add1328874fcb799cc508db1d7b6a880b2d0acea.py new file mode 100644 index 0000000..30f89af --- /dev/null +++ b/tests/small_samples/f09192915e250c0e1630b5d9add1328874fcb799cc508db1d7b6a880b2d0acea.py @@ -0,0 +1 @@ +assert self is None or frame, () \ No newline at end of file diff --git a/tests/small_samples/fbc2436c3fa3e8e8693fdbc22c440b1327007cff3d5bb0bb489140a2a1cee81f.py b/tests/small_samples/fbc2436c3fa3e8e8693fdbc22c440b1327007cff3d5bb0bb489140a2a1cee81f.py new file mode 100644 index 0000000..4072e91 --- /dev/null +++ b/tests/small_samples/fbc2436c3fa3e8e8693fdbc22c440b1327007cff3d5bb0bb489140a2a1cee81f.py @@ -0,0 +1,4 @@ +async def test_contextmanager_except(): + async with woohoo as x: + pass + self \ No newline at end of file diff --git a/tests/small_samples/fddc42a6ffcb9e7fd5933f732a6291f06941ca01c4f3385f96244bb5603ac7b9.py b/tests/small_samples/fddc42a6ffcb9e7fd5933f732a6291f06941ca01c4f3385f96244bb5603ac7b9.py new file mode 100644 index 0000000..8d4cb14 --- /dev/null +++ b/tests/small_samples/fddc42a6ffcb9e7fd5933f732a6291f06941ca01c4f3385f96244bb5603ac7b9.py @@ -0,0 +1,6 @@ +try: + pass +except tokenize as msg: + pass +finally: + pass \ No newline at end of file diff --git a/tests/small_samples/ffd93515dbe0bc61779aafb3cdf11e4c32d229e120139bfc38d3ea54b95a76e3.py b/tests/small_samples/ffd93515dbe0bc61779aafb3cdf11e4c32d229e120139bfc38d3ea54b95a76e3.py new file mode 100644 index 0000000..971b0a8 --- /dev/null +++ b/tests/small_samples/ffd93515dbe0bc61779aafb3cdf11e4c32d229e120139bfc38d3ea54b95a76e3.py @@ -0,0 +1 @@ +# -*- coding: uft-8 -*- diff --git a/tests/small_samples/nan.py b/tests/small_samples/nan.py new file mode 100644 index 0000000..de924dc --- /dev/null +++ b/tests/small_samples/nan.py @@ -0,0 +1,11 @@ +print(1e+300 * 1e+300 * 0) + +# tuple becomes a LOAD_CONST +print((1e+300 * 1e+300 * 0,5)) + +# complex numbers too +print(1e+300 * 1e+300 * 0+5j) + + +def nan_in_function(): + print(1e+300 * 1e+300 * 0) diff --git a/tests/test_features.py b/tests/test_features.py new file mode 100644 index 0000000..693c2bc --- /dev/null +++ b/tests/test_features.py @@ -0,0 +1,537 @@ +import ast +import inspect + +import pytest +import re +from contextlib import contextmanager + +from tests.utils import fexec + +from executing import Source +import executing.executing +import json +import os + +import sys + + +def calling_expr(): + frame = inspect.currentframe() + assert ( + frame is not None + and frame.f_back is not None + and frame.f_back.f_back is not None + ) + ex = Source.executing(frame.f_back.f_back) + assert ex.node is not None + + return ex.node + + +def something(value): + expr = calling_expr() + assert isinstance(expr, ast.Call) and expr.func.id == "something" + if sys.version_info < (3, 8): + assert value == expr.args[0].n + else: + assert value == expr.args[0].value + + +def generate_markdown_report(): + if sys.version_info < (3, 6): + return + + from markdownTable import markdownTable + + directory = os.path.abspath(os.path.join(__file__, os.pardir, "features")) + + def version_key(version): + match = re.fullmatch(r"(\w*)(\d+)\.(\d+)\.json", version) + assert match + + py, major, minor = match.groups() + + return (py, int(major), int(minor)) + + report_files = sorted(os.listdir(directory), key=version_key) + reports = {} + for report_file in report_files: + filename = os.path.join(directory, report_file) + with open(filename) as file: + print(filename) + reports[report_file[:-5]] = {k: dict(v) for k, v in json.load(file)} + + rows = sorted({key for report in reports.values() for key in report}) + + data = [] + + details = ["## Details"] + + for row in rows: + function = globals()[row] + header, doc = inspect.getdoc(function).split("\n", 1) + + sub_rows = [] + for report in reports.values(): + for key in report.get(row, []): + if key not in sub_rows: + sub_rows.append(key) + + detail_data = [ + dict( + name="`%s`" % k, + **{ + name: (":heavy_check_mark:" if report[row][k] else ":x:") + if row in report and k in report[row] + else "" + for name, report in reports.items() + } + ) + for k in sub_rows + ] + + details += [ + "", + "### " + header, + doc, + "\n\n", + markdownTable(detail_data) + .setParams(quote=False, row_sep="markdown") + .getMarkdown(), + ] + + def symbol(values): + if all(values): + return ":heavy_check_mark:" + elif not any(values): + return ":x:" + else: + return "(:white_check_mark:)" + + row_data = {"name": header} + row_data.update( + { + name: symbol(report[row].values()) if row in report else "" + for name, report in reports.items() + } + ) + + data.append(row_data) + + features_filename = os.path.abspath( + os.path.join(__file__, os.pardir, os.pardir, "Features.md") + ) + + with open(features_filename, "w") as file: + file.write("# Features\n## Overview\n") + file.write( + "The following table lists the supported places where executing can be used to map to a ast-node.\n" + ) + file.write( + markdownTable(data).setParams(quote=False, row_sep="markdown").getMarkdown() + ) + file.write("\n") + + file.write("\n".join(details)) + + +@pytest.fixture(scope="session") +def report_file(): + result = {} + + yield result + + py = "pypy" if "pypy" in sys.version.lower() else "" + version = py + ".".join(map(str, sys.version_info[:2])) + + filename = os.path.abspath( + os.path.join(__file__, os.pardir, "features", version + ".json") + ) + + with open(filename, "w") as file: + file.write(json.dumps(sorted(result.items()), indent=2)) + + +@pytest.fixture +def report(report_file, request): + function = request.function + report_file[function.__name__] = [] + + @contextmanager + def sub_report(name): + try: + yield + except AssertionError: + report_file[function.__name__].append((name, False)) + else: + report_file[function.__name__].append((name, True)) + + old_testing = executing.executing.TESTING + executing.executing.TESTING = False + yield sub_report + executing.executing.TESTING = old_testing + + # report_file[function.__name__]={"result":request.node.rep_call.passed} + + +operators = [ + ("+", "add", ast.Add), + ("-", "sub", ast.Sub), + ("/", "truediv", ast.Div), + ("//", "floordiv", ast.FloorDiv), + ("*", "mul", ast.Mult), + ("%", "mod", ast.Mod), + ("**", "pow", ast.Pow), + ("<<", "lshift", ast.LShift), + (">>", "rshift", ast.RShift), + ("|", "or", ast.BitOr), + ("&", "and", ast.BitAnd), + ("^", "xor", ast.BitXor), +] + +if sys.version_info >= (3, 5): + operators.append(("@", "matmul", ast.MatMult)) + + +def test_binary_operators(report): + """ + binary operators + + the node of an binary operation can be obtained inside a `__add__` or other binary operator. + """ + + class Test: + pass + + for _, op, ast_op in operators: + + def op_func(self, other, ast_op=ast_op): + expr = calling_expr() + assert isinstance(expr, ast.BinOp) and isinstance(expr.op, ast_op), expr + + setattr(Test, "__%s__" % op, op_func) + + t = Test() + + for op, _, _ in operators: + with report(op.replace("|", "\\|")): + fexec("from __future__ import division\nt %s 5" % op) + + +def test_inplace_binary_operators(report): + """ + inplace binary operators + + the node of an binary operation can be obtained inside a `__iadd__` or other binary operator. + """ + + class Test: + pass + + for _, op, ast_op in operators: + + def iop_func(self, other, ast_op=ast_op): + expr = calling_expr() + assert isinstance(expr, ast.AugAssign) and isinstance(expr.op, ast_op), expr + return self + + setattr(Test, "__i%s__" % op, iop_func) + + t = Test() + + for op, _, _ in operators: + with report(op.replace("|", "\\|") + "="): + fexec("from __future__ import division\nt %s= 5" % op) + + +def test_reverse_binary_operators(report): + """ + reverse binary operators + + the node of an binary operation can be obtained inside a `__radd__` or other binary operator. + """ + + class Test: + pass + + for _, op, ast_op in operators: + + def op_func(self, other, ast_op=ast_op): + expr = calling_expr() + assert isinstance(expr, ast.BinOp) and isinstance(expr.op, ast_op), expr + + setattr(Test, "__r%s__" % op, op_func) + + t = Test() + + for op, _, _ in operators: + with report(op.replace("|", "\\|")): + fexec("from __future__ import division\n5 %s t" % op) + + +def test_call(report): + """ + `a(arguments...)` + + the node of an binary operation can be obtained inside function or `__call__` operator. + """ + + class Test: + def __call__(self): + expr = calling_expr() + assert isinstance(expr, ast.Call) and expr.func.id == "t" + + t = Test() + with report("call"): + t() + + +def test_getattr(report): + """ + `obj.attr` + + the node can be accessed inside the `__getattr__` function. + """ + + class Test: + def __getattr__(self, name): + expr = calling_expr() + assert isinstance(expr, ast.Attribute) or isinstance(expr, ast.Call) + + t = Test() + with report("obj.attr"): + t.attr + + with report('getattr(obj,"attr")'): + getattr(t, "attr") + + +def test_setattr(report): + """ + `obj.attr = 5` + + the node can be accessed inside the `__setattr__` function. + """ + + class Test: + def __setattr__(self, name, value): + expr = calling_expr() + assert isinstance(expr, ast.Attribute) or isinstance(expr, ast.Call) + + t = Test() + with report("t.attr = 5"): + t.attr = 5 + with report("t.attr, g.attr= 5, 5"): + t.attr, t.other = 5, 5 + with report('setattr(t,"attr",5)'): + getattr(t, "attr", 5) + + +def test_delattr(report): + """ + `del obj.attr` + + the node can be accessed inside the `__delattr__` function. + """ + + class Test: + def __delattr__(self, name): + expr = calling_expr() + assert isinstance(expr, ast.Attribute) or isinstance(expr, ast.Call) + + t = Test() + with report("del obj.attr"): + del t.attr + with report('delattr(obj,"attr")'): + delattr(t, "attr") + + +def test_getitem(report): + """ + `obj[index]` + + the node can be accessed inside the `__getitem__` function. + """ + + class Test: + def __getitem__(self, index): + expr = calling_expr() + assert isinstance(expr, ast.Subscript) + + t = Test() + with report("obj[index]"): + t[5] + + with report("obj[start:end]"): + t[5:10] + + +def test_setitem(report): + """ + `obj[index]=value` + + the node can be accessed inside the `__setitem__` function. + """ + + class Test: + def __setitem__(self, index, value): + expr = calling_expr() + assert isinstance(expr, ast.Subscript) + + t = Test() + with report("obj[index]=value"): + t[5] = 5 + + with report("obj[start:end]=value"): + t[5:10] = 5 + + +def test_delitem(report): + """ + `del obj[index]` + + the node can be accessed inside the `__delitem__` function. + """ + + class Test: + def __delitem__(self, index): + expr = calling_expr() + assert isinstance(expr, ast.Subscript) + + t = Test() + with report("del obj[index]"): + del t[5] + + with report("del obj[start:end]"): + del t[5:10] + + +def test_with(report): + """ + `with contextmanager:` + + __enter__ and __exit__ + """ + + class Test: + def __enter__(self): + with report("__enter__"): + expr = calling_expr() + assert isinstance(expr, ast.With) + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + with report("__exit__"): + expr = calling_expr() + assert isinstance(expr, ast.With) + + with Test() as t: + pass + + +def test_known_issues(report): + """ + known issues + + some known issues + """ + + with report("same generator"): + [something(1) for _ in range(5)], [something(1) for _ in range(5)] + + +def test_format_string(report): + """ + format string + + expressions inside format strings + """ + if sys.version_info >= (3, 6): + with report("format string"): + fexec('f"{something(1)}{something(2)}"') + + +def test_compare_ops(report): + """ + compare ops + + map compare ops: + + * `t<5`: `__lt__` resolves to `ast.Compare(ops=[ast.Lt], ...)` + * `5", ">=", "!=", "=="): + with report(op): + fexec("t %s 5" % op) + fexec("5 %s t" % op) + + for op in ("in", "not in"): + with report(op): + fexec("5 %s t" % op) + + test_exact = False + + with report('assert 5= (3, 11): + def not_found_prior_new_algo(self): + if use_new_algo: from contextlib import nullcontext return nullcontext() @@ -163,10 +164,10 @@ def test_setattr(self): str([None for tester.a, (tester.b, tester.c) in [(1, (2, 3))]]) - with self.not_found_prior_311(): + with self.not_found_prior_new_algo(): tester.a = tester.a = 1 - with self.not_found_prior_311(): + with self.not_found_prior_new_algo(): tester.a, tester.a = 1, 2 def test_setitem(self): @@ -174,10 +175,10 @@ def test_setitem(self): tester[:2] = 3 tester['a'], tester.b = 8, 9 - with self.not_found_prior_311(): + with self.not_found_prior_new_algo(): tester['a'] = tester['b'] = 1 - with self.not_found_prior_311(): + with self.not_found_prior_new_algo(): tester['a'], tester['b'] = 1, 2 def test_comprehensions(self): @@ -193,7 +194,7 @@ def test_comprehensions(self): if sys.version_info >= (3, 11): str([{tester(x) for x in [1]}, {tester(x) for x in [2]}]) else: - with self.assertRaises(NotOneValueFound): + with self.assertRaises((NotOneValueFound,MultipleMatches)): str([{tester(x) for x in [1]}, {tester(x) for x in [2]}]) def test_lambda(self): @@ -284,7 +285,7 @@ def test_many_calls(self): node = new_node else: self.assertIs(node, new_node) - self.assertLess(time.time() - start, 1) + self.assertLess(time.time() - start, 2) def test_many_source_for_filename_calls(self): source = None @@ -394,11 +395,7 @@ def test_qualname(self): def test_extended_arg(self): source = 'tester(6)\n%s\ntester(9)' % list(range(66000)) - _, filename = tempfile.mkstemp() - code = compile(source, filename, 'exec') - with open(filename, 'w') as outfile: - outfile.write(source) - exec(code) + fexec(source) def test_only(self): for n in range(5): @@ -429,83 +426,83 @@ def test_attr(self): str((c.x.x, c.x.y, c.y.x, c.y.y, c.x.asd, c.y.qwe)) def test_store_attr_multiline(self): - if sys.version_info >= (3,11): - tester.x \ - .y = 1 + tester.x \ + .y = 1 - tester.x. \ - y = 2 + tester.x. \ + y = 2 - tester \ - .x.y = 3 + tester \ + .x.y = 3 - tester. \ - x.y = 4 + tester. \ + x.y = 4 - tester \ - . \ - x \ - . \ - y \ - = \ - 4 - - tester \ - . \ - x \ - . \ + tester \ + . \ + x \ + . \ y \ - = \ - 4 - (tester - . - x - . - y - ) = 4 - + = \ + 4 + + tester \ + . \ + x \ + . \ + y \ + = \ + 4 + (tester + . + x + . + y + ) = 4 + + @only_new_algo def test_del_attr_multiline(self): - if sys.version_info >= (3,11): - del tester.x \ - .y - del tester.x. \ - y + del tester.x \ + .y + + del tester.x. \ + y - del tester \ - .x.y + del tester \ + .x.y - del tester. \ - x.y + del tester. \ + x.y def test_method_call_multiline(self): - if sys.version_info >= (3,11): - tester.method( - tester, - ).other_method( - 5 - ) - tester.a().b()\ - .c().d()\ - .e(tester.x1().x2() - .y1() - .y2()).foo.bar.spam() + tester.method( + tester, + ).other_method( + 5 + ) - assert 5== tester.a\ - (tester).\ - b(5) + tester.a().b()\ + .c().d()\ + .e(tester.x1().x2() + .y1() + .y2()).foo.bar.spam() + + assert 5== tester.a\ + (tester).\ + b(5) def test_call_things(self): # call things which are no methods or functions - if sys.version_info >= (3,11): - tester[5](5) - tester.some[5](5) - (tester+tester)(2) + tester[5](5) + tester.some[5](5) + + (tester+tester)(2) - tester(tester)(5) - tester.some(tester)(5) + tester(tester)(5) + tester.some(tester)(5) def test_traceback(self): try: @@ -517,14 +514,10 @@ def test_traceback(self): self.assertEqual(ex.text(), "134895 / 0") def test_retry_cache(self): - _, filename = tempfile.mkstemp() def check(x): source = 'tester(6)\n%s\ntester(9)' % list(range(x)) - code = compile(source, filename, 'exec') - with open(filename, 'w') as outfile: - outfile.write(source) - exec(code, globals(), locals()) + fexec(source) check(3) check(5) @@ -561,18 +554,22 @@ def foo(): foo() closure_not_defined_yet = 1 # noqa + @only_new_algo def test_with(self): - if sys.version_info >= (3, 11): - with tester: - pass + #TODO only __exit__ does not work + if sys.version_info >= (3,8) and not sys.version_info >= (3,10): + pytest.xfail() - with tester as a, tester() as b, tester.tester() as c: - a(b(c())) + with tester: + pass + with tester as a, tester() as b, tester.tester() as c: + a(b(c())) + + @only_new_algo def test_listcomp(self): - if sys.version_info >= (3, 11): - result = [calling_expression() for e in [1]] - self.assertIsInstance(result[0], ast.ListComp) + result = [calling_expression() for e in [1]] + self.assertIsInstance(result[0], ast.ListComp) def test_decorator_cache_instruction(self): frame = inspect.currentframe() @@ -587,6 +584,36 @@ def deco(f): def foo(): pass + def test_cpython_issue_100537(self): + def executing_working(): + frame = inspect.currentframe().f_back + ex = Source.executing(frame) + return ( + isinstance(ex.node, ast.Call) + and ex.node.func.id == executing_working.__name__ + ) + + assert executing_working() + + def broken_code(): + # this breaks executing in 3.7 - 3.10 + # see: https://github.com/python/cpython/issues/100537 + + # fmt: off + #unused = [v for v in [5]] == [ + # v for v in [5] + #] + # fmt: on + + #assert not executing_working() + + def something(): + assert executing_working() + + something() + + broken_code() + def is_unary_not(node): return isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not) @@ -682,6 +709,9 @@ def test_small_samples(full_filename, result_filename): "508ccd0dcac13ecee6f0cea939b73ba5319c780ddbb6c496be96fe5614871d4a", "fc6eb521024986baa84af2634f638e40af090be4aa70ab3c22f3d022e8068228", "42a37b8a823eb2e510b967332661afd679c82c60b7177b992a47c16d81117c8a", + "acc1d8132f178c485aa2238b87ad8831b1c4b3cd3a4189ae3e82399edfb45903", + "8fe94ee74887b182542b2781c81e4bbb90e9db6e923f453f53f12dd33bf35c70", + "469d6657206073f52501ca7a3376add6c909057479278dcd6b0453bd6da0fd76", ] skip_annotations = [ @@ -689,7 +719,7 @@ def test_small_samples(full_filename, result_filename): "9b3db37076d3c7c76bdfd9badcc70d8047584433e1eea89f45014453d58bbc43", ] - if any(s in full_filename for s in skip_sentinel) and sys.version_info < (3, 11): + if any(s in full_filename for s in skip_sentinel) and not use_new_algo: pytest.xfail("SentinelNodeFinder does not find some of the nodes (maybe a bug)") if any(s in full_filename for s in skip_annotations) and sys.version_info < (3, 7): @@ -702,6 +732,8 @@ def test_small_samples(full_filename, result_filename): ): pytest.skip("recursion takes to long in 3.5") + + TestFiles().check_filename(full_filename, check_names=True) @@ -729,6 +761,10 @@ def test_sample_files(self, full_filename, result_filename): assert result == json.load(infile) + @pytest.mark.skipif( + NodeFinder.__name__ == "SentinelNodeFinder", + reason="The SentinelNodeFinder has problems in some situations (see skip_sentinel)", + ) def test_module_files(self): self.start_time = time.time() @@ -778,7 +814,11 @@ def check_filename(self, filename, check_names): # increase the recursion limit in testing mode, because there are files out there with large ast-nodes # example: tests/small_samples/1656dc52edd2385921104de7bb255ca369713f4b8c034ebeba5cf946058109bc.py sys.setrecursionlimit(3000) - source = Source.for_filename(filename) + try: + source = Source.for_filename(filename) + except SyntaxError: + print("skip %s" % filename) + return if source.tree is None: # we could not parse this file (maybe wrong python version) @@ -787,8 +827,15 @@ def check_filename(self, filename, check_names): print("check %s"%filename) - if PY3 and sys.version_info < (3, 11): - code = compile(source.text, filename, "exec", dont_inherit=True) + # TODO: try to fix this later for the new_algo case + if PY3 and not use_new_algo: + try: + code = compile(source.text, filename, "exec", dont_inherit=True) + except SyntaxError: + print("skip %s" % filename) + return + + for subcode, qualname in find_qualnames(code): if not qualname.endswith(">"): code_qualname = source.code_qualname(subcode) @@ -825,7 +872,7 @@ def check_filename(self, filename, check_names): for node, values in nodes.items(): # skip some cases cases - if sys.version_info < (3, 11): + if not use_new_algo: if is_unary_not(node): continue @@ -837,7 +884,7 @@ def check_filename(self, filename, check_names): if isinstance(ctx, ast.Store): # Assignment to attributes and subscripts is less magical # but can also fail fairly easily, so we can't guarantee - # that every node can be identified with some instruction. + # that every node can identified with some instruction. continue if isinstance(ctx, (ast.Del, getattr(ast, 'Param', ()))): @@ -897,7 +944,7 @@ def check_filename(self, filename, check_names): if isinstance(first_node.parent,(ast.If,ast.Assert,ast.While,ast.IfExp)) and first_node is first_node.parent.test: continue - if isinstance(first_node.parent,(ast.match_case)) and first_node is first_node.parent.guard: + if sys.version_info >= (3,10) and isinstance(first_node.parent,(ast.match_case)) and first_node is first_node.parent.guard: continue if isinstance(first_node.parent,(ast.BoolOp,ast.Return)): continue @@ -952,7 +999,18 @@ def check_filename(self, filename, check_names): ): continue - if sys.version_info >= (3, 10): + if sys.version_info >= (3,9): + if isinstance(node,ast.Compare): + # Compare is often mapped to if + continue + + if sys.version_info >= (3,8) and not sys.version_info >= (3,9) and isinstance(node, ast.Name) and isinstance(node.parent,ast.AugAssign): + continue + + + if use_new_algo: + correct = len(values) >= 1 + elif sys.version_info >= (3, 10): correct = len(values) >= 1 elif sys.version_info >= (3, 9) and in_finally(node): correct = len(values) > 1 @@ -969,7 +1027,9 @@ def p(*args): p("in file:", filename) p("correct:", correct) p("len(values):", len(values)) - p("values:", values) + p("values:") + for v in values: + p(v[0]) p("deadcode:", is_deadcode(node)) p() @@ -1034,19 +1094,75 @@ def inspect(bc): return result + if sys.version_info >= (3,8) and not sys.version_info >= (3,10): + + def instructions(self, instructions): + # python <3.10 generates deadcode + # this code reports only reachable instructions + # this would otherwise be a problem, because the index_node_finder can not map unreachable instructions + from executing._index_node_finder import end_of_block + + instructions = list(instructions) + todo = [0] + done = set() + result=[] + + jump_opcodes = dis.hasjrel + dis.hasjabs + + while todo: + offset = todo.pop() + if offset in done: + continue + done.add(offset) + + index = offset // 2 + + if index >= len(instructions): + continue + + inst = instructions[index] + + if inst.opcode in jump_opcodes: + if inst.argval not in done: + todo.append(inst.argval) + + result.append((index, inst)) + + if inst.opcode in end_of_block: + continue + + if inst.opname in ("JUMP_ABSOLUTE", "JUMP_FORWARD"): + continue + + next_offset = offset + 2 + if next_offset not in done: + todo.append(next_offset) + + return sorted(result) + + else: + + def instructions(self, instructions): + return enumerate(instructions) + def check_code(self, code, nodes, decorators, check_names): linestarts = dict(dis.findlinestarts(code)) instructions = list(get_instructions(code)) lineno = None - for inst_index, inst in enumerate(instructions): + + child_codes = [] + for inst_index, inst in self.instructions(instructions): if hasattr(self, "start_time"): if time.time() - self.start_time > 45 * 60: # Avoid travis time limit of 50 minutes raise TimeOut - py11 = sys.version_info >= (3, 11) lineno = linestarts.get(inst.offset, lineno) + + if isinstance(inst.argval, type(code)): + child_codes.append(inst.argval) + if not ( inst.opname.startswith( ( @@ -1074,7 +1190,7 @@ def check_code(self, code, nodes, decorators, check_names): * check_names ) or ( - py11 + use_new_algo and inst.opname in ( "LOAD_GLOBAL", @@ -1118,6 +1234,7 @@ def check_code(self, code, nodes, decorators, check_names): if inst.positions.lineno == None: continue + frame = C() frame.f_lasti = inst.offset frame.f_code = code @@ -1133,6 +1250,20 @@ def check_code(self, code, nodes, decorators, check_names): except KnownIssue: continue + except OverflowError as e: + if str(e)=="line number table is too long": + pytest.skip() + + except MultipleMatches as matches: + + # this safes us from "node without associated Bytecode" error + for node in matches.nodes: + nodes[node].append((inst, frame.__dict__)) + continue + + except NoMatch: + continue + except VerifierFailure as e: print("VerifierFailure:") @@ -1170,10 +1301,9 @@ def check_code(self, code, nodes, decorators, check_names): raise - except Exception as e: + except (NotOneValueFound if use_new_algo else Exception) as e: # continue for every case where this can be an known issue - if py11: exact_options = [] for node in ast.walk(source.tree): @@ -1242,6 +1372,8 @@ def check_code(self, code, nodes, decorators, check_names): ): continue + + # report more information for debugging print("mapping failed") @@ -1305,7 +1437,7 @@ def check_code(self, code, nodes, decorators, check_names): # `argval` isn't set for all relevant instructions in python 2 # The relation between `ast.Name` and `argval` is already # covered by the verifier and much more complex in python 3.11 - if isinstance(node, ast.Name) and (PY3 or inst.argval) and not py11: + if isinstance(node, ast.Name) and (PY3 or inst.argval) and not use_new_algo: assert inst.argval == node.id, (inst, ast.dump(node)) if ex.decorator: @@ -1316,10 +1448,9 @@ def check_code(self, code, nodes, decorators, check_names): yield [inst.opname, node_string(source, ex.decorator or node)] # do not use code.co_consts because they can contain deadcode https://github.com/python/cpython/issues/96833 - for inst in instructions: - if isinstance(inst.argval, type(code)): - for x in self.check_code(inst.argval, nodes, decorators, check_names=check_names): - yield x + for code in child_codes: + for x in self.check_code(code, nodes, decorators, check_names=check_names): + yield x def node_string(source, node): @@ -1362,6 +1493,9 @@ def is_literal(node): def is_pattern(node): + if not sys.version_info >= (3,10): + return False + while not isinstance(node, ast.pattern): if hasattr(node, "parent"): node = node.parent diff --git a/tests/test_pytest.py b/tests/test_pytest.py index b89aed5..35307f1 100644 --- a/tests/test_pytest.py +++ b/tests/test_pytest.py @@ -163,7 +163,7 @@ def test_bad_linecache(): if sys.version_info >= (3, 11): - from executing._position_node_finder import mangled_name + from executing._base_node_finder import mangled_name from textwrap import indent import dis diff --git a/tests/utils.py b/tests/utils.py index 0e20ecf..5bab70d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,7 @@ import sys import ast import inspect +import tempfile from collections import namedtuple import executing.executing @@ -14,8 +15,10 @@ executing.executing.TESTING = 1 from executing import Source +from executing.executing import use_new_algo non_existing_argument=object() +checking=False class Tester(object): def __init__(self): @@ -42,33 +45,40 @@ def get_executing(self, frame): return Source.executing(frame) def check(self, node, value): + global checking + frame = inspect.currentframe().f_back.f_back - result = eval( - compile(ast.Expression(node), frame.f_code.co_filename, 'eval'), - frame.f_globals, - frame.f_locals, - ) + checking=True + try: + result = eval( + compile(ast.Expression(node), frame.f_code.co_filename, 'eval'), + frame.f_globals, + frame.f_locals, + ) + finally: + checking=False assert result == value, (result, value) def __call__(self, arg=non_existing_argument, check_func=True): - ex = self.get_executing(inspect.currentframe().f_back) - if ex.decorator: - assert {ex.node} == ex.statements - self.decorators.append(ex.node.decorator_list.index(ex.decorator)) - else: - call = ex.node - if arg is non_existing_argument: - assert len(call.args)==0 + if not checking: + ex = self.get_executing(inspect.currentframe().f_back) + if ex.decorator: + assert {ex.node} == ex.statements + self.decorators.append(ex.node.decorator_list.index(ex.decorator)) else: - self.check(call.args[0], arg) - - if check_func: - self.check(call.func, self) - if ( - isinstance(call.parent, (ast.ClassDef, ast.FunctionDef)) - and call in call.parent.decorator_list - ): - return self + call = ex.node + if arg is non_existing_argument: + assert len(call.args)==0 + else: + self.check(call.args[0], arg) + + if check_func: + self.check(call.func, self) + if ( + isinstance(call.parent, (ast.ClassDef, ast.FunctionDef)) + and call in call.parent.decorator_list + ): + return self if arg is non_existing_argument: return tester @@ -76,20 +86,22 @@ def __call__(self, arg=non_existing_argument, check_func=True): return arg def __getattr__(self, item): - parent_frame=inspect.currentframe().f_back + if not checking: + parent_frame=inspect.currentframe().f_back - # pytest is accessing tester to check if it is a test function - if "_pytest" not in parent_frame.f_code.co_filename: - node = self.get_node(ast.Attribute) - self.check(node.value, self) - assert node.attr == item + # pytest is accessing tester to check if it is a test function + if "_pytest" not in parent_frame.f_code.co_filename: + node = self.get_node(ast.Attribute) + self.check(node.value, self) + assert node.attr == item return self def __getitem__(self, item): - node = self.get_node(ast.Subscript) - self.check(node.value, self) - self.check(subscript_item(node), item) + if not checking: + node = self.get_node(ast.Subscript) + self.check(node.value, self) + self.check(subscript_item(node), item) return self def __setattr__(self, name, value): @@ -120,9 +132,10 @@ def __setitem__(self, key, value): return self def __add__(self, other): - node = self.get_node(ast.BinOp) - self.check(node.left, self) - self.check(node.right, other) + if not checking: + node = self.get_node(ast.BinOp) + self.check(node.left, self) + self.check(node.right, other) return self __pow__ = __mul__ = __sub__ = __add__ @@ -143,7 +156,7 @@ def __lt__(self, other): __ne__ = __ge__ = __lt__ def __bool__(self): - if sys.version_info >= (3, 11): + if use_new_algo: self.get_node(ast.BoolOp) return False else: @@ -213,3 +226,18 @@ def end_position(obj): obj=obj.body[-1] return SourcePosition(obj.end_lineno, obj.end_col_offset) + +def no_pytest_vars(d): + return {k: v for k, v in d.items() if not k.startswith("@py")} + +def fexec(source): + frame = inspect.currentframe() + assert frame + frame = frame.f_back + assert frame + + _, filename = tempfile.mkstemp() + with open(filename, "w") as outfile: + outfile.write(source) + code = compile(source, filename, "exec") + exec(code, no_pytest_vars(frame.f_globals), no_pytest_vars(frame.f_locals)) diff --git a/tox.ini b/tox.ini index c2c5429..5201df1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,28 @@ [tox] -envlist = py27,py35,py36,py37,py38,py39,py310,py311,pypy2,pypy35,pypy36 +envlist = cov_clear,py27,py35,py36,py37,py38,py39,py310,py311,pypy2,pypy35,pypy36,generate_report, mypy-py{27via39,35,36,37,38,39,310,311} + +[testenv:cov_clear] +basepython=python3.10 +deps = coverage +commands= coverage erase [testenv] commands = - pytest tests {posargs} + coverage run -m pytest tests {posargs} extras = tests passenv = FIX_EXECUTING_TESTS ADD_EXECUTING_TESTS EXECUTING_SLOW_TESTS +setenv = + COVERAGE_PROCESS_START={env:PWD}/setup.cfg + +[testenv:cov_report] +basepython=python3.10 +deps = coverage +commands = + coverage combine + coverage html [testenv:generate_small_sample-py{27,35,36,37,38,39,310,311}] extras = tests @@ -26,3 +40,35 @@ deps= mutmut commands= python tests/mutmut_workflow.py + + +[testenv:analyse-py{38,39,310,311}] +deps= + rich +commands= + python tests/analyse.py {posargs} + +[testenv:mypy-py311] +deps=mypy==0.971 +commands=python -m mypy executing + +[testenv:mypy-py{38,39,310}] +deps=mypy==0.971 +commands=python -m mypy executing --exclude=executing/_position_node_finder.py + +[testenv:mypy-py{35,36,37}] +deps=mypy==0.910 +commands=python -m mypy executing --exclude 'executing/_.*_node_finder.py' + +[testenv:mypy-py27via39] +basepython=python3.9 +deps=mypy[python2]==0.910 +commands=python -m mypy --py2 executing --exclude 'executing/_.*_node_finder.py' + +[testenv:generate_report] +basepython=python3.10 +depends=py27,py35,py36,py37,py38,py39,py310,py311,pypy2,pypy35,pypy36 +deps= + py-markdown-table +commands= + python -m tests.test_features