Merge branch 'master' of github.com:rocky/python-uncompyle6

This commit is contained in:
rocky 2021-10-21 15:54:08 -04:00
commit 3e5f963c64
70 changed files with 537 additions and 474 deletions

31
.github/workflows/osx.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: uncompyle6 (osx)
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: macos-latest
strategy:
matrix:
os: [macOS]
python-version: [3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Until the next xdis release
pip install git+git://github.com/rocky/python-xdis.git#egg=xdis
pip install -e .
pip install -r requirements-dev.txt
- name: Test uncompyle6
run: |
make check

30
.github/workflows/ubuntu.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: uncompyle6 (ubuntu)
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Until the next xdis release
pip install git+git://github.com/rocky/python-xdis.git#egg=xdis
pip install -e .
pip install -r requirements-dev.txt
- name: Test uncompyle6
run: |
make check

31
.github/workflows/windows.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: uncompyle6 (windows)
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: macos-latest
strategy:
matrix:
os: [windows]
python-version: [3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Until the next xdis release
pip install git+git://github.com/rocky/python-xdis.git#egg=xdis
pip install -e .
pip install -r requirements-dev.txt
- name: Test uncompyle6
run: |
make check

View File

@ -13,6 +13,8 @@ matrix:
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
install:
# Remove the next line when xdis 6.0.0 is released
# - pip install git://github.com/rocky/python-xdis.git#egg=xdis
- pip install -e .
- pip install -r requirements-dev.txt

View File

@ -34,7 +34,7 @@
# Things that change more often go here.
copyright = """
Copyright (C) 2015-2020 Rocky Bernstein <rb@dustyfeet.com>.
Copyright (C) 2015-2021 Rocky Bernstein <rb@dustyfeet.com>.
"""
classifiers = [
@ -70,7 +70,7 @@ entry_points = {
]
}
ftp_url = None
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 5.0.4, <5.1.0"]
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 6.0.0, < 6.1.0"]
license = "GPL3"
mailing_list = "python-debugger@googlegroups.com"

View File

@ -8,7 +8,7 @@ owd=$(pwd)
trap finish EXIT
cd $(dirname ${BASH_SOURCE[0]})
if ! source ./pyenv-newer-versions ; then
if ! source ./pyenv-newest-versions ; then
exit $?
fi
if ! source ./setup-master.sh ; then

View File

@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS='3.5.9 3.6.12 2.6.9 3.3.7 2.7.18 3.2.6 3.1.5 3.4.10 3.7.9 3.8.5'
export PYVERSIONS='3.6.15 3.7.12 pyston-2.3 3.8.11'

View File

@ -1,79 +0,0 @@
environment:
global:
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
# /E:ON and /V:ON options are not enabled in the batch script intepreter
# See: http://stackoverflow.com/a/13751649/163740
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
matrix:
# Pre-installed Python versions, which Appveyor may upgrade to
# a later point release.
# See: http://www.appveyor.com/docs/installed-software#python
# - PYTHON: "C:\\Python27"
# PYTHON_VERSION: "2.7.x"
# PYTHON_ARCH: "32"
- PYTHON: "C:\\Python27-x64"
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "64"
# - PYTHON: "C:\\Python26"
# PYTHON_VERSION: "2.6.x"
# PYTHON_ARCH: "32"
# - PYTHON: "C:\\Python26-x64"
# PYTHON_VERSION: "2.6.x"
# PYTHON_ARCH: "64"
install:
# We need wheel installed to build wheels
- "%PYTHON%\\python.exe -m pip install wheel"
# Install Python (from the official .msi of http://python.org) and pip when
# not already installed.
- ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "SET HOME=."
# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "%PYTHON%\\python.exe -m pip install --disable-pip-version-check --user --upgrade pip"
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- "%CMD_IN_ENV% pip install git+git://github.com/rocky/python-uncompyle6.git#egg=uncompyle6-3.6.6"
- "%CMD_IN_ENV% pip install -r requirements.txt"
build_script:
# Build the compiled extension
- "%CMD_IN_ENV% python setup.py build"
test_script:
# Run the project tests
- "%CMD_IN_ENV% python test/test_pyenvlib.py --native --syntax-verify"
after_test:
# If tests are successful, create binary packages for the project.
- "%CMD_IN_ENV% python setup.py bdist_wininst"
- "%CMD_IN_ENV% python setup.py bdist_msi"
- ps: "ls dist"
artifacts:
# Archive the generated packages in the ci.appveyor.com build report.
- path: dist\*
#on_success:
# - TODO: upload the content of dist/*.whl to a public wheelhouse
#

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
from uncompyle6 import PYTHON_VERSION, IS_PYPY
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
from uncompyle6.scanner import get_scanner
def bug(state, slotstate):
if state:
@ -21,8 +21,8 @@ def bug_loop(disassemble, tb=None):
def test_if_in_for():
code = bug.__code__
scan = get_scanner(PYTHON_VERSION)
if 2.7 <= PYTHON_VERSION <= 3.0 and not IS_PYPY:
scan = get_scanner(PYTHON_VERSION_TRIPLE)
if (2, 7) <= PYTHON_VERSION_TRIPLE < (3, 1) and not IS_PYPY:
scan.build_instructions(code)
fjt = scan.find_jump_targets(False)
@ -51,7 +51,7 @@ def test_if_in_for():
# previous bug was not mistaking while-loop for if-then
{'start': 48, 'end': 67, 'type': 'while-loop'}]
elif 3.2 < PYTHON_VERSION <= 3.4:
elif (3, 2) < PYTHON_VERSION_TRIPLE <= (3, 4):
scan.build_instructions(code)
fjt = scan.find_jump_targets(False)
assert {69: [66], 63: [18]} == fjt
@ -62,6 +62,6 @@ def test_if_in_for():
{'end': 59, 'type': 'for-loop', 'start': 31},
{'end': 63, 'type': 'for-else', 'start': 62}]
else:
print("FIXME: should fix for %s" % PYTHON_VERSION)
print("FIXME: should fix for %s" % ".".join([str(v) for v in PYTHON_VERSION_TRIPLE]))
assert True
return

View File

@ -1,7 +1,7 @@
import re
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY # , PYTHON_VERSION
from uncompyle6.parser import get_python_parser, python_parser
from uncompyle6.scanner import get_scanner
from xdis.version_info import PYTHON_VERSION_TRIPLE, PYTHON3, IS_PYPY
def test_grammar():
@ -16,19 +16,19 @@ def test_grammar():
p.dump_grammar(),
)
p = get_python_parser(PYTHON_VERSION, is_pypy=IS_PYPY)
p = get_python_parser(PYTHON_VERSION_TRIPLE, is_pypy=IS_PYPY)
(lhs, rhs, tokens, right_recursive, dup_rhs) = p.check_sets()
# We have custom rules that create the below
expect_lhs = set(["pos_arg"])
if PYTHON_VERSION < 3.8:
if PYTHON_VERSION < 3.7:
if PYTHON_VERSION_TRIPLE < (3, 8):
if PYTHON_VERSION_TRIPLE < (3, 7):
expect_lhs.add("attribute")
expect_lhs.add("get_iter")
if PYTHON_VERSION > 3.7 or PYTHON_VERSION < 3.0:
if PYTHON_VERSION_TRIPLE >= (3, 8) or PYTHON_VERSION_TRIPLE < (3, 0):
expect_lhs.add("stmts_opt")
else:
expect_lhs.add("async_with_as_stmt")
@ -38,15 +38,15 @@ def test_grammar():
expect_right_recursive = set([("designList", ("store", "DUP_TOP", "designList"))])
if PYTHON_VERSION <= 3.6:
if PYTHON_VERSION_TRIPLE[:2] <= (3, 6):
unused_rhs.add("call")
if PYTHON_VERSION > 2.6:
if PYTHON_VERSION_TRIPLE >= (2, 7):
expect_lhs.add("kvlist")
expect_lhs.add("kv3")
unused_rhs.add("dict")
if PYTHON_VERSION < 3.7 and PYTHON_VERSION != 2.7:
if PYTHON_VERSION_TRIPLE < (3, 7) and PYTHON_VERSION_TRIPLE[:2] != (2, 7):
# NOTE: this may disappear
expect_lhs.add("except_handler_else")
@ -60,8 +60,8 @@ def test_grammar():
""".split()
)
)
if PYTHON_VERSION >= 3.0:
if PYTHON_VERSION < 3.7:
if PYTHON_VERSION_TRIPLE >= (3, 0):
if PYTHON_VERSION_TRIPLE < (3, 7):
expect_lhs.add("annotate_arg")
expect_lhs.add("annotate_tuple")
unused_rhs.add("mkfunc_annotate")
@ -69,7 +69,7 @@ def test_grammar():
unused_rhs.add("dict_comp")
unused_rhs.add("classdefdeco1")
unused_rhs.add("tryelsestmtl")
if PYTHON_VERSION >= 3.5:
if PYTHON_VERSION_TRIPLE >= (3, 5):
expect_right_recursive.add(
(("l_stmts", ("lastl_stmt", "come_froms", "l_stmts")))
)
@ -80,7 +80,7 @@ def test_grammar():
expect_lhs.add("kwarg")
# FIXME
if PYTHON_VERSION < 3.8:
if PYTHON_VERSION_TRIPLE < (3, 8):
assert expect_lhs == set(lhs)
assert unused_rhs == set(rhs)
@ -103,7 +103,7 @@ def test_grammar():
print(k, reduced_dup_rhs[k])
# assert not reduced_dup_rhs, reduced_dup_rhs
s = get_scanner(PYTHON_VERSION, IS_PYPY)
s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
ignore_set = set(
"""
JUMP_BACK CONTINUE
@ -116,12 +116,13 @@ def test_grammar():
RETURN_END_IF RETURN_END_IF_LAMBDA RETURN_VALUE_LAMBDA RETURN_LAST
""".split()
)
if 2.6 <= PYTHON_VERSION <= 2.7:
if (2, 6) <= PYTHON_VERSION_TRIPLE <= (2, 7):
opcode_set = set(s.opc.opname).union(ignore_set)
if PYTHON_VERSION == 2.6:
if PYTHON_VERSION_TRIPLE[:2] == (2, 6):
opcode_set.add("THEN")
check_tokens(tokens, opcode_set)
elif PYTHON_VERSION == 3.4:
elif PYTHON_VERSION_TRIPLE[:2] == (3, 4):
ignore_set.add("LOAD_CLASSNAME")
ignore_set.add("STORE_LOCALS")
opcode_set = set(s.opc.opname).union(ignore_set)
@ -132,7 +133,7 @@ def test_dup_rule():
import inspect
python_parser(
PYTHON_VERSION,
PYTHON_VERSION_TRIPLE,
inspect.currentframe().f_code,
is_pypy=IS_PYPY,
parser_debug={

View File

@ -9,12 +9,13 @@ import tempfile
import functools
# uncompyle6 / xdis
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY, code_deparse
from uncompyle6 import code_deparse
from xdis.version_info import PYTHON_VERSION_TRIPLE, PYTHON3, IS_PYPY
# TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there
from xdis import Bytecode, get_opcode
opc = get_opcode(PYTHON_VERSION, IS_PYPY)
opc = get_opcode(PYTHON_VERSION_TRIPLE, IS_PYPY)
Bytecode = functools.partial(Bytecode, opc=opc)
import six
@ -127,7 +128,7 @@ def validate_uncompyle(text, mode="exec"):
original_text = text
deparsed = code_deparse(
original_code, out=six.StringIO(), version=PYTHON_VERSION, compile_mode=mode
original_code, out=six.StringIO(), version=PYTHON_VERSION_TRIPLE, compile_mode=mode
)
uncompyled_text = deparsed.text
uncompyled_code = compile(uncompyled_text, "<string>", "exec")

View File

@ -31,6 +31,8 @@ import xdis.magics as magics
# ----- configure this for your needs
python_versions = [v for v in magics.python_versions if re.match("^[0-9.]+$", v)]
print(python_versions)
sys.exit(0)
# FIXME: we should remove Python versions that we don't support.
# These include Jython, and Python bytecode changes pre release.

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2016, 2818-2020 by Rocky Bernstein
# Copyright (c) 2015-2016, 2818-2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@ -47,7 +47,7 @@ def disco(version, co, out=None, is_pypy=False):
# store final output stream for case of error
real_out = out or sys.stdout
print("# Python %s" % version, file=real_out)
print("# Python %s" % ".".join([str(v) for v in version]), file=real_out)
if co.co_filename:
print("# Embedded file name: %s" % co.co_filename, file=real_out)

View File

@ -642,107 +642,109 @@ def get_python_parser(
# If version is a string, turn that into the corresponding float.
if isinstance(version, str):
version = py_str2float(version)
version = tuple([int(v) for v in version.split(".")[:2]])
version = version[:2]
# FIXME: there has to be a better way...
# We could do this as a table lookup, but that would force us
# in import all of the parsers all of the time. Perhaps there is
# a lazy way of doing the import?
if version < 3.0:
if version < 2.2:
if version == 1.0:
if version < (3, 0):
if version < (2, 2):
if version == (1, 0):
import uncompyle6.parsers.parse10 as parse10
if compile_mode == "exec":
p = parse10.Python10Parser(debug_parser)
else:
p = parse10.Python01ParserSingle(debug_parser)
elif version == 1.1:
elif version == (1, 1):
import uncompyle6.parsers.parse11 as parse11
if compile_mode == "exec":
p = parse11.Python11Parser(debug_parser)
else:
p = parse11.Python11ParserSingle(debug_parser)
if version == 1.2:
if version == (1, 2):
import uncompyle6.parsers.parse12 as parse12
if compile_mode == "exec":
p = parse12.Python12Parser(debug_parser)
else:
p = parse12.Python12ParserSingle(debug_parser)
if version == 1.3:
if version == (1, 3):
import uncompyle6.parsers.parse13 as parse13
if compile_mode == "exec":
p = parse13.Python13Parser(debug_parser)
else:
p = parse13.Python13ParserSingle(debug_parser)
elif version == 1.4:
elif version == (1, 4):
import uncompyle6.parsers.parse14 as parse14
if compile_mode == "exec":
p = parse14.Python14Parser(debug_parser)
else:
p = parse14.Python14ParserSingle(debug_parser)
elif version == 1.5:
elif version == (1, 5):
import uncompyle6.parsers.parse15 as parse15
if compile_mode == "exec":
p = parse15.Python15Parser(debug_parser)
else:
p = parse15.Python15ParserSingle(debug_parser)
elif version == 1.6:
elif version == (1, 6):
import uncompyle6.parsers.parse16 as parse16
if compile_mode == "exec":
p = parse16.Python16Parser(debug_parser)
else:
p = parse16.Python16ParserSingle(debug_parser)
elif version == 2.1:
elif version == (2, 1):
import uncompyle6.parsers.parse21 as parse21
if compile_mode == "exec":
p = parse21.Python21Parser(debug_parser)
else:
p = parse21.Python21ParserSingle(debug_parser)
elif version == 2.2:
elif version == (2, 2):
import uncompyle6.parsers.parse22 as parse22
if compile_mode == "exec":
p = parse22.Python22Parser(debug_parser)
else:
p = parse22.Python22ParserSingle(debug_parser)
elif version == 2.3:
elif version == (2, 3):
import uncompyle6.parsers.parse23 as parse23
if compile_mode == "exec":
p = parse23.Python23Parser(debug_parser)
else:
p = parse23.Python23ParserSingle(debug_parser)
elif version == 2.4:
elif version == (2, 4):
import uncompyle6.parsers.parse24 as parse24
if compile_mode == "exec":
p = parse24.Python24Parser(debug_parser)
else:
p = parse24.Python24ParserSingle(debug_parser)
elif version == 2.5:
elif version == (2, 5):
import uncompyle6.parsers.parse25 as parse25
if compile_mode == "exec":
p = parse25.Python25Parser(debug_parser)
else:
p = parse25.Python25ParserSingle(debug_parser)
elif version == 2.6:
elif version == (2, 6):
import uncompyle6.parsers.parse26 as parse26
if compile_mode == "exec":
p = parse26.Python26Parser(debug_parser)
else:
p = parse26.Python26ParserSingle(debug_parser)
elif version == 2.7:
elif version == (2, 7):
import uncompyle6.parsers.parse27 as parse27
if compile_mode == "exec":
@ -762,63 +764,63 @@ def get_python_parser(
else:
import uncompyle6.parsers.parse3 as parse3
if version == 3.0:
if version == (3, 0):
import uncompyle6.parsers.parse30 as parse30
if compile_mode == "exec":
p = parse30.Python30Parser(debug_parser)
else:
p = parse30.Python30ParserSingle(debug_parser)
elif version == 3.1:
elif version == (3, 1):
import uncompyle6.parsers.parse31 as parse31
if compile_mode == "exec":
p = parse31.Python31Parser(debug_parser)
else:
p = parse31.Python31ParserSingle(debug_parser)
elif version == 3.2:
elif version == (3, 2):
import uncompyle6.parsers.parse32 as parse32
if compile_mode == "exec":
p = parse32.Python32Parser(debug_parser)
else:
p = parse32.Python32ParserSingle(debug_parser)
elif version == 3.3:
elif version == (3, 3):
import uncompyle6.parsers.parse33 as parse33
if compile_mode == "exec":
p = parse33.Python33Parser(debug_parser)
else:
p = parse33.Python33ParserSingle(debug_parser)
elif version == 3.4:
elif version == (3, 4):
import uncompyle6.parsers.parse34 as parse34
if compile_mode == "exec":
p = parse34.Python34Parser(debug_parser)
else:
p = parse34.Python34ParserSingle(debug_parser)
elif version == 3.5:
elif version == (3, 5):
import uncompyle6.parsers.parse35 as parse35
if compile_mode == "exec":
p = parse35.Python35Parser(debug_parser)
else:
p = parse35.Python35ParserSingle(debug_parser)
elif version == 3.6:
elif version == (3, 6):
import uncompyle6.parsers.parse36 as parse36
if compile_mode == "exec":
p = parse36.Python36Parser(debug_parser)
else:
p = parse36.Python36ParserSingle(debug_parser)
elif version == 3.7:
elif version == (3, 7):
import uncompyle6.parsers.parse37 as parse37
if compile_mode == "exec":
p = parse37.Python37Parser(debug_parser)
else:
p = parse37.Python37ParserSingle(debug_parser)
elif version == 3.8:
elif version == (3, 8):
import uncompyle6.parsers.parse38 as parse38
if compile_mode == "exec":

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 Rocky Bernstein
# Copyright (c) 2015-2021 Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
# Copyright (c) 1999 John Aycock
@ -350,7 +350,7 @@ class Python2Parser(PythonParser):
],
customize,
)
if self.version >= 2.7:
if self.version >= (2, 7):
self.add_unique_rule(
"dict_comp_func ::= BUILD_MAP_n LOAD_FAST FOR_ITER store "
"comp_iter JUMP_BACK RETURN_VALUE RETURN_LAST",
@ -577,7 +577,7 @@ class Python2Parser(PythonParser):
customize,
)
if self.version >= 2.7:
if self.version >= (2, 7):
if i > 0:
prev_tok = tokens[i - 1]
if prev_tok == "LOAD_DICTCOMP":

View File

@ -94,7 +94,7 @@ class Python24Parser(Python25Parser):
""")
super(Python24Parser, self).customize_grammar_rules(tokens, customize)
self.remove_rules_24()
if self.version == 2.4:
if self.version[:2] == (2, 4):
self.check_reduce['nop_stmt'] = 'tokens'
def reduce_is_invalid(self, rule, ast, tokens, first, last):

View File

@ -89,7 +89,7 @@ class Python25Parser(Python26Parser):
return_stmt_lambda LAMBDA_MARKER
""")
super(Python25Parser, self).customize_grammar_rules(tokens, customize)
if self.version == 2.5:
if self.version[:2] == (2, 5):
self.check_reduce["try_except"] = "tokens"
self.check_reduce["aug_assign1"] = "AST"
self.check_reduce["ifelsestmt"] = "AST"

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2020 Rocky Bernstein
# Copyright (c) 2017-2021 Rocky Bernstein
"""
spark grammar differences over Python2 for Python 2.6.
"""
@ -390,7 +390,7 @@ class Python26Parser(Python2Parser):
# For now, we won't let the 2nd 'expr' be a "if_exp_not"
# However in < 2.6 where we don't have if/else expression it *can*
# be.
if self.version >= 2.6 and ast[2][0] == "if_exp_not":
if self.version >= (2, 6) and ast[2][0] == "if_exp_not":
return True
test_index = last
@ -425,7 +425,7 @@ class Python26Parser(Python2Parser):
# since the operand can be a relative offset rather than
# an absolute offset.
setup_inst = self.insts[self.offset2inst_index[tokens[first].offset]]
if self.version <= 2.2 and tokens[last] == "COME_FROM":
if self.version <= (2, 2) and tokens[last] == "COME_FROM":
last += 1
return tokens[last-1].off2int() > setup_inst.argval
elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 Rocky Bernstein
# Copyright (c) 2015-2021 Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@ -536,7 +536,7 @@ class Python3Parser(PythonParser):
# FIXME: What's the deal with the two rules? Different Python versions?
# Different situations? Note that the above rule is based on the CALL_FUNCTION
# token found, while this one doesn't.
if self.version < 3.6:
if self.version < (3, 6):
call_function = self.call_fn_name(call_fn_tok)
args_pos, args_kw = self.get_pos_kw(call_fn_tok)
rule = "build_class ::= LOAD_BUILD_CLASS mkfunc %s" "%s" % (
@ -591,7 +591,7 @@ class Python3Parser(PythonParser):
# Note: 3.5+ have subclassed this method; so we don't handle
# 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here.
if is_pypy and self.version >= 3.6:
if is_pypy and self.version >= (3, 6):
if token == "CALL_FUNCTION":
token.kind = self.call_fn_name(token)
rule = (
@ -625,7 +625,7 @@ class Python3Parser(PythonParser):
"""Python 3.3 added a an addtional LOAD_STR before MAKE_FUNCTION and
this has an effect on many rules.
"""
if self.version >= 3.3:
if self.version >= (3, 3):
if PYTHON3 or not self.is_pypy:
load_op = "LOAD_STR "
else:
@ -774,7 +774,7 @@ class Python3Parser(PythonParser):
rule = "kvlist_n ::="
self.add_unique_rule(rule, "kvlist_n", 1, customize)
rule = "dict ::= BUILD_MAP_n kvlist_n"
elif self.version >= 3.5:
elif self.version >= (3, 5):
if not opname.startswith("BUILD_MAP_WITH_CALL"):
# FIXME: Use the attr
# so this doesn't run into exponential parsing time.
@ -1059,7 +1059,7 @@ class Python3Parser(PythonParser):
args_pos, args_kw, annotate_args = token.attr
# FIXME: Fold test into add_make_function_rule
if self.version < 3.3:
if self.version < (3, 3):
j = 1
else:
j = 2
@ -1121,7 +1121,7 @@ class Python3Parser(PythonParser):
kwargs_str = ""
# Note order of kwargs and pos args changed between 3.3-3.4
if self.version <= 3.2:
if self.version <= (3, 2):
if annotate_args > 0:
rule = (
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE %s"
@ -1138,7 +1138,7 @@ class Python3Parser(PythonParser):
"pos_arg " * args_pos,
opname,
)
elif self.version == 3.3:
elif self.version == (3, 3):
if annotate_args > 0:
rule = (
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s"
@ -1156,7 +1156,7 @@ class Python3Parser(PythonParser):
opname,
)
elif self.version >= 3.4:
elif self.version >= (3, 4):
if PYTHON3 or not self.is_pypy:
load_op = "LOAD_STR"
else:
@ -1190,7 +1190,7 @@ class Python3Parser(PythonParser):
)
self.add_unique_rule(rule, opname, token.attr, customize)
if self.version < 3.4:
if self.version < (3, 4):
rule = "mkfunc ::= %sload_closure LOAD_CODE %s" % (
"expr " * args_pos,
opname,
@ -1200,7 +1200,7 @@ class Python3Parser(PythonParser):
pass
elif opname_base.startswith("MAKE_FUNCTION"):
# DRY with MAKE_CLOSURE
if self.version >= 3.6:
if self.version >= (3, 6):
# The semantics of MAKE_FUNCTION in 3.6 are totally different from
# before.
args_pos, args_kw, annotate_args, closure = token.attr
@ -1262,7 +1262,7 @@ class Python3Parser(PythonParser):
if self.is_pypy or (
i >= 2 and tokens[i - 2] == "LOAD_LISTCOMP"
):
if self.version >= 3.6:
if self.version >= (3, 6):
# 3.6+ sometimes bundles all of the
# 'exprs' in the rule above into a
# tuple.
@ -1293,12 +1293,12 @@ class Python3Parser(PythonParser):
)
continue
if self.version < 3.6:
if self.version < (3, 6):
args_pos, args_kw, annotate_args = token.attr
else:
args_pos, args_kw, annotate_args, closure = token.attr
if self.version < 3.3:
if self.version < (3, 3):
j = 1
else:
j = 2
@ -1339,7 +1339,7 @@ class Python3Parser(PythonParser):
else:
kwargs = "kwargs"
if self.version < 3.3:
if self.version < (3, 3):
# positional args after keyword args
rule = "mkfunc ::= %s %s%s%s" % (
kwargs,
@ -1353,7 +1353,7 @@ class Python3Parser(PythonParser):
"LOAD_CODE ",
opname,
)
elif self.version == 3.3:
elif self.version == (3, 3):
# positional args after keyword args
rule = "mkfunc ::= %s %s%s%s" % (
kwargs,
@ -1361,7 +1361,7 @@ class Python3Parser(PythonParser):
"LOAD_CODE LOAD_STR ",
opname,
)
elif self.version > 3.5:
elif self.version >= (3, 6):
# positional args before keyword args
rule = "mkfunc ::= %s%s %s%s" % (
"pos_arg " * args_pos,
@ -1369,7 +1369,7 @@ class Python3Parser(PythonParser):
"LOAD_CODE LOAD_STR ",
opname,
)
elif self.version > 3.3:
elif self.version >= (3, 4):
# positional args before keyword args
rule = "mkfunc ::= %s%s %s%s" % (
"pos_arg " * args_pos,
@ -1386,7 +1386,7 @@ class Python3Parser(PythonParser):
self.add_unique_rule(rule, opname, token.attr, customize)
if re.search("^MAKE_FUNCTION.*_A", opname):
if self.version >= 3.6:
if self.version >= (3, 6):
rule = (
"mkfunc_annotate ::= %s%sannotate_tuple LOAD_CODE LOAD_STR %s"
% (
@ -1404,11 +1404,11 @@ class Python3Parser(PythonParser):
opname,
)
)
if self.version >= 3.3:
if self.version >= (3, 3):
# Normally we remove EXTENDED_ARG from the opcodes, but in the case of
# annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function.
# Yes this is a little hacky
if self.version == 3.3:
if self.version == (3, 3):
# 3.3 puts kwargs before pos_arg
pos_kw_tuple = (
("kwargs " * args_kw),
@ -1548,7 +1548,7 @@ class Python3Parser(PythonParser):
"try_except": tryexcept,
}
if self.version == 3.6:
if self.version == (3, 6):
self.reduce_check_table["and"] = and_check
self.check_reduce["and"] = "AST"
@ -1560,7 +1560,7 @@ class Python3Parser(PythonParser):
self.check_reduce["ifelsestmtc"] = "AST"
self.check_reduce["ifstmt"] = "AST"
self.check_reduce["ifstmtl"] = "AST"
if self.version == 3.6:
if self.version == (3, 6):
self.reduce_check_table["iflaststmtl"] = iflaststmt
self.check_reduce["iflaststmt"] = "AST"
self.check_reduce["iflaststmtl"] = "AST"
@ -1568,7 +1568,7 @@ class Python3Parser(PythonParser):
self.check_reduce["testtrue"] = "tokens"
if not PYTHON3:
self.check_reduce["kwarg"] = "noAST"
if self.version < 3.6 and not self.is_pypy:
if self.version < (3, 6) and not self.is_pypy:
# 3.6+ can remove a JUMP_FORWARD which messes up our testing here
# Pypy we need to go over in better detail
self.check_reduce["try_except"] = "AST"
@ -1595,11 +1595,11 @@ class Python3Parser(PythonParser):
elif lhs == "kwarg":
arg = tokens[first].attr
return not (isinstance(arg, str) or isinstance(arg, unicode))
elif lhs in ("iflaststmt", "iflaststmtl") and self.version == 3.6:
elif lhs in ("iflaststmt", "iflaststmtl") and self.version[:2] == (3, 6):
return ifstmt(self, lhs, n, rule, ast, tokens, first, last)
elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):
# FIXME: go over what's up with 3.0. Evetually I'd like to remove RETURN_END_IF
if self.version <= 3.0 or tokens[last] == "RETURN_END_IF":
if self.version <= (3, 0) or tokens[last] == "RETURN_END_IF":
return False
if ifstmt(self, lhs, n, rule, ast, tokens, first, last):
return True
@ -1641,7 +1641,7 @@ class Python3Parser(PythonParser):
if while1stmt(self, lhs, n, rule, ast, tokens, first, last):
return True
if self.version == 3.0:
if self.version == (3, 0):
return False
if 0 <= last < len(tokens) and tokens[last] in (
@ -1683,7 +1683,7 @@ class Python3Parser(PythonParser):
if last == n:
return False
# 3.8+ Doesn't have SETUP_LOOP
return self.version < 3.8 and tokens[first].attr > tokens[last].offset
return self.version < (3, 8) and tokens[first].attr > tokens[last].offset
elif rule == (
"ifelsestmt",
(

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017, 2019 Rocky Bernstein
# Copyright (c) 2016-2017, 2019, 2021 Rocky Bernstein
"""
spark grammar differences over Python 3.4 for Python 3.5.
"""
@ -157,7 +157,7 @@ class Python35Parser(Python34Parser):
# FIXME: I suspect this is wrong for 3.6 and 3.5, but
# I haven't verified what the 3.7ish fix is
elif opname == 'BUILD_MAP_UNPACK_WITH_CALL':
if self.version < 3.7:
if self.version < (3, 7):
self.addRule("expr ::= unmapexpr", nop_func)
nargs = token.attr % 256
map_unpack_n = "map_unpack_%s" % nargs
@ -168,7 +168,7 @@ class Python35Parser(Python34Parser):
call_token = tokens[i+1]
rule = 'call ::= expr unmapexpr ' + call_token.kind
self.addRule(rule, nop_func)
elif opname == 'BEFORE_ASYNC_WITH' and self.version < 3.8:
elif opname == 'BEFORE_ASYNC_WITH' and self.version < (3, 8):
# Some Python 3.5+ async additions
rules_str = """
stmt ::= async_with_stmt

View File

@ -396,7 +396,7 @@ class Python36Parser(Python35Parser):
starred ::= expr
call_ex ::= expr starred CALL_FUNCTION_EX
""", nop_func)
if self.version >= 3.6:
if self.version >= (3, 6):
if 'BUILD_MAP_UNPACK_WITH_CALL' in self.seen_ops:
self.addRule("""
expr ::= call_ex_kw

View File

@ -1292,7 +1292,7 @@ class Python37Parser(Python37BaseParser):
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
"""
if self.version < 3.8:
if self.version < (3, 8):
rules_str += """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
LOAD_CONST

View File

@ -209,7 +209,7 @@ class Python37BaseParser(PythonParser):
stmt ::= async_with_as_stmt
"""
if self.version < 3.8:
if self.version < (3, 8):
rules_str += """
stmt ::= async_with_stmt SETUP_ASYNC_WITH
c_stmt ::= c_async_with_stmt SETUP_ASYNC_WITH
@ -1034,7 +1034,7 @@ class Python37BaseParser(PythonParser):
POP_BLOCK LOAD_CONST COME_FROM_WITH
with_suffix
"""
if self.version < 3.8:
if self.version < (3, 8):
rules_str += """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
LOAD_CONST

View File

@ -10,7 +10,7 @@ def except_handler(self, lhs, n, rule, ast, tokens, first, last):
# FIXME: Figure out why this doesn't work on
# bytecode-1.4/anydbm.pyc
if self.version == 1.4:
if self.version[:2] == (1, 4):
return False
# Make sure come froms all come from within "except_handler".

View File

@ -1,8 +1,8 @@
# Copyright (c) 2020 Rocky Bernstein
# Copyright (c) 2020-2021 Rocky Bernstein
def except_handler_else(self, lhs, n, rule, ast, tokens, first, last):
# FIXME: expand this to other versions
if self.version not in (2.7, 3.5):
if self.version[:2] not in ((2, 7), (3, 5)):
return False
if tokens[first] in ("JUMP_FORWARD", "JUMP_ABSOLUTE"):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2020 Rocky Bernstein
# Copyright (c) 2020-2021 Rocky Bernstein
from uncompyle6.scanners.tok import Token
@ -158,7 +158,7 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
# just the last one.
if len(ast) == 5:
end_come_froms = ast[-1]
if end_come_froms.kind != "else_suite" and self.version >= 3.0:
if end_come_froms.kind != "else_suite" and self.version >= (3, 0):
if end_come_froms == "opt_come_from_except" and len(end_come_froms) > 0:
end_come_froms = end_come_froms[0]
if not isinstance(end_come_froms, Token):
@ -169,12 +169,12 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
# FIXME: There is weirdness in the grammar we need to work around.
# we need to clean up the grammar.
if self.version < 3.0:
if self.version < (3, 0):
last_token = ast[-1]
else:
last_token = tokens[last]
if last_token == "COME_FROM" and tokens[first].offset > last_token.attr:
if self.version < 3.0 and self.insts[self.offset2inst_index[last_token.attr]].opname != "SETUP_LOOP":
if self.version < (3, 0) and self.insts[self.offset2inst_index[last_token.attr]].opname != "SETUP_LOOP":
return True
testexpr = ast[0]
@ -191,7 +191,7 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
if last == n:
last -= 1
jmp = if_condition[1]
if self.version > 2.6:
if self.version >= (2, 7):
jmp_target = jmp[0].attr
else:
jmp_target = int(jmp[0].pattr)

View File

@ -45,7 +45,7 @@ def or_check(self, lhs, n, rule, ast, tokens, first, last):
return True
# If the jmp is backwards
if last_token == "POP_JUMP_IF_FALSE" and not self.version in (2.7, 3.5, 3.6):
if last_token == "POP_JUMP_IF_FALSE" and not self.version[:2] in ((2, 7), (3, 5), (3, 6)):
if last_token.attr < last_token_offset:
# For a backwards loop, well compare to the instruction *after*
# then POP_JUMP...

View File

@ -1,9 +1,9 @@
# Copyright (c) 2020 Rocky Bernstein
# Copyright (c) 2020-2021 Rocky Bernstein
def testtrue(self, lhs, n, rule, ast, tokens, first, last):
# FIXME: make this work for all versions
if self.version != 3.7:
if self.version[:2] != (3, 7):
return False
if rule == ("testtrue", ("expr", "jmp_true")):
pjit = tokens[min(last - 1, n - 2)]

View File

@ -20,4 +20,4 @@ def while1elsestmt(self, lhs, n, rule, ast, tokens, first, last):
# not while1else. Also do for whileTrue?
last += 1
# 3.8+ Doesn't have SETUP_LOOP
return self.version < 3.8 and tokens[first].attr > tokens[last].off2int()
return self.version < (3, 8) and tokens[first].attr > tokens[last].off2int()

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016, 2018-2020 by Rocky Bernstein
# Copyright (c) 2016, 2018-2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@ -33,36 +33,38 @@ import xdis
from xdis import Bytecode, canonic_python_version, code2num, instruction_size, extended_arg_val, next_offset
# The byte code versions we support.
# Note: these all have to be floats
# Note: these all have to be tuples of 2 ints
PYTHON_VERSIONS = frozenset(
(
1.0,
1.1,
1.3,
1.4,
1.5,
1.6,
2.1,
2.2,
2.3,
2.4,
2.5,
2.6,
2.7,
3.0,
3.1,
3.2,
3.3,
3.4,
3.5,
3.6,
3.7,
3.8,
3.9,
(1, 0),
(1, 1),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(2, 1),
(2, 2),
(2, 3),
(2, 4),
(2, 5),
(2, 6),
(2, 7),
(3, 0),
(3, 1),
(3, 2),
(3, 3),
(3, 4),
(3, 5),
(3, 6),
(3, 7),
(3, 8),
)
)
CANONIC2VERSION = dict((canonic_python_version[str(v)], v) for v in PYTHON_VERSIONS)
CANONIC2VERSION = dict(
(canonic_python_version[".".join(str(v) for v in python_version)], python_version)
for python_version in PYTHON_VERSIONS
)
# Magic changed mid version for Python 3.5.2. Compatibility was added for
# the older 3.5 interpreter magic.
@ -103,15 +105,15 @@ class Scanner(object):
self.show_asm = show_asm
self.is_pypy = is_pypy
if version in PYTHON_VERSIONS:
if version[:2] in PYTHON_VERSIONS:
if is_pypy:
v_str = "opcode_%spypy" % (int(version * 10))
v_str = "opcode_%spypy" % ("".join([str(v) for v in version]))
else:
v_str = "opcode_%s" % (int(version * 10))
v_str = "opcode_%s" % ("".join([str(v) for v in version]))
exec("from xdis.opcodes import %s" % v_str)
exec("self.opc = %s" % v_str)
else:
raise TypeError("%s is not a Python version I know about" % version)
raise TypeError("%s is not a Python version I know about" % ".".join([str(v) for v in version]))
self.opname = self.opc.opname
@ -144,7 +146,7 @@ class Scanner(object):
# Offset: lineno pairs, only for offsets which start line.
# Locally we use list for more convenient iteration using indices
if self.version > 1.4:
if self.version > (1, 4):
linestarts = list(self.opc.findlinestarts(code_obj))
else:
linestarts = [[0, 1]]
@ -533,8 +535,8 @@ def get_scanner(version, is_pypy=False, show_asm=None):
version = CANONIC2VERSION[canonic_version]
# Pick up appropriate scanner
if version in PYTHON_VERSIONS:
v_str = "%s" % (int(version * 10))
if version[:2] in PYTHON_VERSIONS:
v_str = "".join([str(v) for v in version[:2]])
try:
import importlib

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016-2017, 2021 by Rocky Bernstein
"""
Python PyPy 2.7 bytecode scanner/deparser
@ -22,5 +22,5 @@ class ScannerPyPy27(scan.Scanner27):
# There are no differences in initialization between
# pypy 2.7 and 2.7
scan.Scanner27.__init__(self, show_asm, is_pypy=True)
self.version = 2.7
self.version = (2, 7)
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017 by Rocky Bernstein
# Copyright (c) 2017, 2021 by Rocky Bernstein
"""
Python PyPy 3.2 decompiler scanner.
@ -18,6 +18,6 @@ class ScannerPyPy32(scan.Scanner32):
# There are no differences in initialization between
# pypy 3.2 and 3.2
scan.Scanner32.__init__(self, show_asm, is_pypy=True)
self.version = 3.2
self.version = (3, 2)
self.opc = opc
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2020 by Rocky Bernstein
# Copyright (c) 2019-2021 by Rocky Bernstein
"""
Python PyPy 3.3 decompiler scanner.
@ -19,6 +19,6 @@ class ScannerPyPy33(scan.Scanner33):
# There are no differences in initialization between
# pypy 3.3 and 3.3
scan.Scanner33.__init__(self, show_asm, is_pypy=True)
self.version = 3.3
self.version = (3, 3)
self.opc = opc
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017, 2019 by Rocky Bernstein
# Copyright (c) 2017, 2019, 2021 by Rocky Bernstein
"""
Python PyPy 3.5 decompiler scanner.
@ -18,5 +18,5 @@ class ScannerPyPy35(scan.Scanner35):
# There are no differences in initialization between
# pypy 3.5 and 3.5
scan.Scanner35.__init__(self, show_asm, is_pypy=True)
self.version = 3.5
self.version = (3, 5)
return

View File

@ -18,5 +18,5 @@ class ScannerPyPy36(scan.Scanner36):
# There are no differences in initialization between
# pypy 3.6 and 3.6
scan.Scanner36.__init__(self, show_asm, is_pypy=True)
self.version = 3.6
self.version = (3, 6)
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019, 2021 by Rocky Bernstein
"""
Python 1.0 bytecode decompiler massaging.
@ -22,7 +22,7 @@ class Scanner10(scan.Scanner11):
scan.Scanner11.__init__(self, show_asm)
self.opc = opcode_10
self.opname = opcode_10.opname
self.version = 1.0
self.version = (1, 0)
return
# def ingest(self, co, classname=None, code_objects={}, show_asm=None):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019, 2021 by Rocky Bernstein
"""
Python 1.1 bytecode decompiler massaging.
@ -22,7 +22,7 @@ class Scanner11(scan.Scanner13): # no scanner 1.2
scan.Scanner13.__init__(self, show_asm)
self.opc = opcode_11
self.opname = opcode_11.opname
self.version = 1.1
self.version = (1, 1)
return
# def ingest(self, co, classname=None, code_objects={}, show_asm=None):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019, 2021 by Rocky Bernstein
"""
Python 1.2 bytecode decompiler massaging.
@ -23,7 +23,7 @@ class Scanner12(scan.Scanner13):
scan.Scanner14.__init__(self, show_asm)
self.opc = opcode_11
self.opname = opcode_11.opname
self.version = 1.2 # Note: is the same as 1.1 bytecode
self.version = (1, 2) # Note: is the same as 1.1 bytecode
return
# def ingest(self, co, classname=None, code_objects={}, show_asm=None):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2019 by Rocky Bernstein
# Copyright (c) 2018-2019, 2021 by Rocky Bernstein
"""
Python 1.3 bytecode decompiler massaging.
@ -24,7 +24,7 @@ class Scanner13(scan.Scanner14):
scan.Scanner14.__init__(self, show_asm)
self.opc = opcode_13
self.opname = opcode_13.opname
self.version = 1.3
self.version = (1, 3)
return
# def ingest(self, co, classname=None, code_objects={}, show_asm=None):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2019 by Rocky Bernstein
# Copyright (c) 2018-2019, 2021 by Rocky Bernstein
"""
Python 1.4 bytecode decompiler massaging.
@ -22,7 +22,7 @@ class Scanner14(scan.Scanner15):
scan.Scanner15.__init__(self, show_asm)
self.opc = opcode_14
self.opname = opcode_14.opname
self.version = 1.4
self.version = (1, 4)
self.genexpr_name = '<generator expression>'
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2018 by Rocky Bernstein
# Copyright (c) 2016-2018, 2021 by Rocky Bernstein
"""
Python 1.5 bytecode decompiler massaging.
@ -22,7 +22,7 @@ class Scanner15(scan.Scanner21):
scan.Scanner21.__init__(self, show_asm)
self.opc = opcode_15
self.opname = opcode_15.opname
self.version = 1.5
self.version = (1, 5)
self.genexpr_name = '<generator expression>'
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019, 2021 by Rocky Bernstein
"""
Python 1.6 bytecode decompiler massaging.
@ -22,7 +22,7 @@ class Scanner16(scan.Scanner21):
scan.Scanner21.__init__(self, show_asm)
self.opc = opcode_16
self.opname = opcode_16.opname
self.version = 1.6
self.version = (1, 6)
self.genexpr_name = '<generator expression>'
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 by Rocky Bernstein
# Copyright (c) 2015-2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
@ -288,7 +288,7 @@ class Scanner2(Scanner):
# last_offset = jump_offset
come_from_name = "COME_FROM"
op_name = self.opname_for_offset(jump_offset)
if op_name.startswith("SETUP_") and self.version == 2.7:
if op_name.startswith("SETUP_") and self.version[:2] == (2, 7):
come_from_type = op_name[len("SETUP_") :]
if come_from_type not in ("LOOP", "EXCEPT"):
come_from_name = "COME_FROM_%s" % come_from_type
@ -350,12 +350,12 @@ class Scanner2(Scanner):
pattr = names[oparg]
elif op in self.opc.JREL_OPS:
# use instead: hasattr(self, 'patch_continue'): ?
if self.version == 2.7:
if self.version[:2] == (2, 7):
self.patch_continue(tokens, offset, op)
pattr = repr(offset + 3 + oparg)
elif op in self.opc.JABS_OPS:
# use instead: hasattr(self, 'patch_continue'): ?
if self.version == 2.7:
if self.version[:2] == (2, 7):
self.patch_continue(tokens, offset, op)
pattr = repr(oparg)
elif op in self.opc.LOCAL_OPS:
@ -515,7 +515,7 @@ class Scanner2(Scanner):
while code[j] == self.opc.JUMP_ABSOLUTE:
j = self.prev[j]
if (
self.version >= 2.3 and self.opname_for_offset(j) == "LIST_APPEND"
self.version >= (2, 3) and self.opname_for_offset(j) == "LIST_APPEND"
): # list comprehension
stmts.remove(s)
continue
@ -529,7 +529,7 @@ class Scanner2(Scanner):
prev = code[self.prev[s]]
if (
prev == self.opc.ROT_TWO
or self.version < 2.7
or self.version < (2, 7)
and prev
in (
self.opc.JUMP_IF_FALSE,
@ -543,7 +543,7 @@ class Scanner2(Scanner):
j = self.prev[s]
while code[j] in self.designator_ops:
j = self.prev[j]
if self.version > 2.1 and code[j] == self.opc.FOR_ITER:
if self.version > (2, 1) and code[j] == self.opc.FOR_ITER:
stmts.remove(s)
continue
last_stmt = s
@ -563,7 +563,7 @@ class Scanner2(Scanner):
jmp = self.prev[self.get_target(except_match)]
# In Python < 2.7 we may have jumps to jumps
if self.version < 2.7 and self.code[jmp] in self.jump_forward:
if self.version < (2, 7) and self.code[jmp] in self.jump_forward:
self.not_continue.add(jmp)
jmp = self.get_target(jmp)
prev_offset = self.prev[except_match]
@ -587,7 +587,7 @@ class Scanner2(Scanner):
op = self.code[i]
if op == self.opc.END_FINALLY:
if count_END_FINALLY == count_SETUP_:
if self.version == 2.7:
if self.version[:2] == (2, 7):
assert self.code[self.prev[i]] in self.jump_forward | frozenset(
[self.opc.RETURN_VALUE]
)
@ -649,7 +649,7 @@ class Scanner2(Scanner):
# Account for the fact that < 2.7 has an explicit
# POP_TOP instruction in the equivalate POP_JUMP_IF
# construct
if self.version < 2.7:
if self.version < (2, 7):
jump_forward_offset = jump_back_offset + 4
return_val_offset1 = self.prev[
self.prev[self.prev[loop_end_offset]]
@ -691,7 +691,7 @@ class Scanner2(Scanner):
jump_back_offset += 1
if_offset = None
if self.version < 2.7:
if self.version < (2, 7):
# Look for JUMP_IF POP_TOP ...
if code[self.prev[next_line_byte]] == self.opc.POP_TOP and (
code[self.prev[self.prev[next_line_byte]]] in self.pop_jump_if
@ -703,7 +703,7 @@ class Scanner2(Scanner):
if if_offset:
loop_type = "while"
self.ignore_if.add(if_offset)
if self.version < 2.7 and (
if self.version < (2, 7) and (
code[self.prev[jump_back_offset]] == self.opc.RETURN_VALUE
):
self.ignore_if.add(self.prev[jump_back_offset])
@ -735,7 +735,7 @@ class Scanner2(Scanner):
setup_target = self.get_target(jump_back_offset, self.opc.JUMP_ABSOLUTE)
if self.version > 2.1 and code[setup_target] in (
if self.version > (2, 1) and code[setup_target] in (
self.opc.FOR_ITER,
self.opc.GET_ITER,
):
@ -745,7 +745,7 @@ class Scanner2(Scanner):
# Look for a test condition immediately after the
# SETUP_LOOP while
if (
self.version < 2.7
self.version < (2, 7)
and self.code[self.prev[next_line_byte]] == self.opc.POP_TOP
):
test_op_offset = self.prev[self.prev[next_line_byte]]
@ -822,7 +822,7 @@ class Scanner2(Scanner):
if target != start_else:
end_else = self.get_target(jmp)
if self.code[jmp] == self.opc.JUMP_FORWARD:
if self.version <= 2.6:
if self.version <= (2, 6):
self.fixed_jumps[jmp] = target
else:
self.fixed_jumps[jmp] = -1
@ -833,7 +833,7 @@ class Scanner2(Scanner):
if end_else != start_else:
r_end_else = self.restrict_to_parent(end_else, parent)
# May be able to drop the 2.7 test.
if self.version == 2.7:
if self.version[:2] == (2, 7):
self.structs.append(
{"type": "try-else", "start": i + 1, "end": r_end_else}
)
@ -861,7 +861,7 @@ class Scanner2(Scanner):
# possibly I am "skipping over" a "pass" or null statement.
test_target = target
if self.version < 2.7:
if self.version < (2, 7):
# Before 2.7 we have to deal with the fact that there is an extra
# POP_TOP that is logically associated with the JUMP_IF's (even though
# the instance set is called "self.pop_jump_if")
@ -981,7 +981,7 @@ class Scanner2(Scanner):
self.fixed_jumps[offset] = fix or match[-1]
return
else:
if self.version < 2.7 and parent["type"] in (
if self.version < (2, 7) and parent["type"] in (
"root",
"for-loop",
"if-then",
@ -997,7 +997,7 @@ class Scanner2(Scanner):
self.fixed_jumps[offset] = match[-1]
return
else: # op != self.opc.PJIT
if self.version < 2.7 and code[offset + 3] == self.opc.POP_TOP:
if self.version < (2, 7) and code[offset + 3] == self.opc.POP_TOP:
assert_offset = offset + 4
else:
assert_offset = offset + 3
@ -1039,7 +1039,7 @@ class Scanner2(Scanner):
if offset in self.ignore_if:
return
if self.version == 2.7:
if self.version == (2, 7):
if (
code[pre_rtarget] == self.opc.JUMP_ABSOLUTE
and pre_rtarget in self.stmts
@ -1109,7 +1109,7 @@ class Scanner2(Scanner):
if_then_maybe = None
if 2.2 <= self.version <= 2.6:
if (2, 2) <= self.version <= (2, 6):
# Take the JUMP_IF target. In an "if/then", it will be
# a POP_TOP instruction and the instruction before it
# will be a JUMP_FORWARD to just after the POP_TOP.
@ -1159,13 +1159,13 @@ class Scanner2(Scanner):
"end": pre_rtarget,
}
elif self.version == 2.7:
elif self.version[:2] == (2, 7):
self.structs.append(
{"type": "if-then", "start": start - 3, "end": pre_rtarget}
)
# FIXME: this is yet another case were we need dominators.
if pre_rtarget not in self.linestarts or self.version < 2.7:
if pre_rtarget not in self.linestarts or self.version < (2, 7):
self.not_continue.add(pre_rtarget)
if rtarget < end_offset:
@ -1194,7 +1194,7 @@ class Scanner2(Scanner):
{"type": "else", "start": rtarget, "end": end_offset}
)
elif code_pre_rtarget == self.opc.RETURN_VALUE:
if self.version == 2.7 or pre_rtarget not in self.ignore_if:
if self.version[:2] == (2, 7) or pre_rtarget not in self.ignore_if:
# Below, 10 is exception-match. If there is an exception
# match in the compare, then this is an exception
# clause not an if-then clause
@ -1207,7 +1207,7 @@ class Scanner2(Scanner):
)
self.thens[start] = rtarget
if (
self.version == 2.7
self.version[:2] == (2, 7)
or code[pre_rtarget + 1] != self.opc.JUMP_FORWARD
):
# The below is a big hack until we get
@ -1220,7 +1220,7 @@ class Scanner2(Scanner):
# instruction before.
self.fixed_jumps[offset] = rtarget
if (
self.version == 2.7
self.version[:2] == (2, 7)
and self.insts[
self.offset2inst_index[pre[pre_rtarget]]
].is_jump_target
@ -1291,7 +1291,7 @@ class Scanner2(Scanner):
# if (op in self.opc.JREL_OPS and
# (self.version < 2.0 or op != self.opc.FOR_ITER)):
label = offset + 3 + oparg
elif self.version == 2.7 and op in self.opc.JABS_OPS:
elif self.version[:2] == (2, 7) and op in self.opc.JABS_OPS:
if op in (
self.opc.JUMP_IF_FALSE_OR_POP,
self.opc.JUMP_IF_TRUE_OR_POP,
@ -1307,7 +1307,7 @@ class Scanner2(Scanner):
# We REALLY REALLY need a better way to handle control flow
# Expecially for < 2.7
if label is not None and label != -1:
if self.version == 2.7:
if self.version[:2] == (2, 7):
# FIXME: rocky: I think we need something like this...
if label in self.setup_loops:
source = self.setup_loops[label]
@ -1342,7 +1342,7 @@ class Scanner2(Scanner):
# handle COME_FROM's from a loop inside if's
# It probably should.
if (
self.version > 2.6
self.version > (2, 6)
or self.code[source] != self.opc.SETUP_LOOP
or self.code[label] != self.opc.JUMP_FORWARD
):
@ -1354,7 +1354,7 @@ class Scanner2(Scanner):
elif (
op == self.opc.END_FINALLY
and offset in self.fixed_jumps
and self.version == 2.7
and self.version[:2] == (2, 7)
):
label = self.fixed_jumps[offset]
targets[label] = targets.get(label, []) + [offset]

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2018 by Rocky Bernstein
# Copyright (c) 2016-2018, 2021 by Rocky Bernstein
"""
Python 2.1 bytecode massaging.
@ -22,6 +22,6 @@ class Scanner21(scan.Scanner22):
scan.Scanner22.__init__(self, show_asm)
self.opc = opcode_21
self.opname = opcode_21.opname
self.version = 2.1
self.version = (2, 1)
self.genexpr_name = '<generator expression>'
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2018 by Rocky Bernstein
# Copyright (c) 2016-2018, 2021 by Rocky Bernstein
"""
Python 2.2 bytecode massaging.
@ -22,7 +22,7 @@ class Scanner22(scan.Scanner23):
scan.Scanner23.__init__(self, show_asm)
self.opc = opcode_22
self.opname = opcode_22.opname
self.version = 2.2
self.version = (2, 2)
self.genexpr_name = '<generator expression>'
self.parent_ingest = self.ingest
self.ingest = self.ingest22

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2018 by Rocky Bernstein
# Copyright (c) 2016-2018, 2021 by Rocky Bernstein
"""
Python 2.3 bytecode massaging.
@ -23,6 +23,6 @@ class Scanner23(scan.Scanner24):
self.opname = opcode_23.opname
# These are the only differences in initialization between
# 2.3-2.6
self.version = 2.3
self.version = (2, 3)
self.genexpr_name = '<generator expression>'
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016-2017, 2021 by Rocky Bernstein
"""
Python 2.4 bytecode massaging.
@ -10,6 +10,7 @@ import uncompyle6.scanners.scanner25 as scan
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_24
JUMP_OPS = opcode_24.JUMP_OPS
# We base this off of 2.5 instead of the other way around
@ -23,6 +24,6 @@ class Scanner24(scan.Scanner25):
# 2.4, 2.5 and 2.6
self.opc = opcode_24
self.opname = opcode_24.opname
self.version = 2.4
self.genexpr_name = '<generator expression>'
self.version = (2, 4)
self.genexpr_name = "<generator expression>"
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2018 by Rocky Bernstein
# Copyright (c) 2015-2018, 2021 by Rocky Bernstein
"""
Python 2.5 bytecode massaging.
@ -24,5 +24,5 @@ class Scanner25(scan.Scanner26):
self.opc = opcode_25
self.opname = opcode_25.opname
scan.Scanner26.__init__(self, show_asm)
self.version = 2.5
self.version = (2, 5)
return

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2017, 2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
@ -40,7 +40,7 @@ JUMP_OPS = opcode_26.JUMP_OPS
class Scanner26(scan.Scanner2):
def __init__(self, show_asm=False):
super(Scanner26, self).__init__(2.6, show_asm)
super(Scanner26, self).__init__((2, 6), show_asm)
# "setup" opcodes
self.setup_ops = frozenset([
@ -213,13 +213,13 @@ class Scanner26(scan.Scanner2):
# CE - Hack for >= 2.5
# Now all values loaded via LOAD_CLOSURE are packed into
# a tuple before calling MAKE_CLOSURE.
if (self.version >= 2.5 and op == self.opc.BUILD_TUPLE and
if (self.version >= (2, 5) and op == self.opc.BUILD_TUPLE and
self.code[self.prev[offset]] == self.opc.LOAD_CLOSURE):
continue
else:
op_name = '%s_%d' % (op_name, oparg)
customize[op_name] = oparg
elif self.version > 2.0 and op == self.opc.CONTINUE_LOOP:
elif self.version > (2, 0) and op == self.opc.CONTINUE_LOOP:
customize[op_name] = 0
elif op_name in """
CONTINUE_LOOP EXEC_STMT LOAD_LISTCOMP LOAD_SETCOMP

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2018 by Rocky Bernstein
# Copyright (c) 2015-2018, 2021 by Rocky Bernstein
"""
Python 2.7 bytecode ingester.
@ -12,77 +12,115 @@ from __future__ import print_function
from uncompyle6.scanners.scanner2 import Scanner2
from uncompyle6 import PYTHON3
if PYTHON3:
import sys
intern = sys.intern
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_27
JUMP_OPS = opcode_27.JUMP_OPs
class Scanner27(Scanner2):
def __init__(self, show_asm=False, is_pypy=False):
super(Scanner27, self).__init__(2.7, show_asm, is_pypy)
super(Scanner27, self).__init__((2, 7), show_asm, is_pypy)
# opcodes that start statements
self.statement_opcodes = frozenset(
self.statement_opcodes | set([
# New in 2.7
self.opc.SETUP_WITH,
self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1,
self.opc.STORE_SLICE_2, self.opc.STORE_SLICE_3,
self.opc.DELETE_SLICE_0, self.opc.DELETE_SLICE_1,
self.opc.DELETE_SLICE_2, self.opc.DELETE_SLICE_3,
]))
self.statement_opcodes
| set(
[
# New in 2.7
self.opc.SETUP_WITH,
self.opc.STORE_SLICE_0,
self.opc.STORE_SLICE_1,
self.opc.STORE_SLICE_2,
self.opc.STORE_SLICE_3,
self.opc.DELETE_SLICE_0,
self.opc.DELETE_SLICE_1,
self.opc.DELETE_SLICE_2,
self.opc.DELETE_SLICE_3,
]
)
)
# opcodes which expect a variable number pushed values and whose
# count is in the opcode. For parsing we generally change the
# opcode name to include that number.
varargs_ops = set([
self.opc.BUILD_LIST, self.opc.BUILD_TUPLE,
self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE,
self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION,
self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR,
self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW,
self.opc.DUP_TOPX, self.opc.RAISE_VARARGS,
# New in Python 2.7
self.opc.BUILD_SET, self.opc.BUILD_MAP])
varargs_ops = set(
[
self.opc.BUILD_LIST,
self.opc.BUILD_TUPLE,
self.opc.BUILD_SLICE,
self.opc.UNPACK_SEQUENCE,
self.opc.MAKE_FUNCTION,
self.opc.CALL_FUNCTION,
self.opc.MAKE_CLOSURE,
self.opc.CALL_FUNCTION_VAR,
self.opc.CALL_FUNCTION_KW,
self.opc.CALL_FUNCTION_VAR_KW,
self.opc.DUP_TOPX,
self.opc.RAISE_VARARGS,
# New in Python 2.7
self.opc.BUILD_SET,
self.opc.BUILD_MAP,
]
)
if is_pypy:
varargs_ops.add(self.opc.CALL_METHOD)
self.varargs_ops = frozenset(varargs_ops)
# "setup" opcodes
self.setup_ops = frozenset([
self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY,
# New in 2.7
self.opc.SETUP_WITH])
self.setup_ops = frozenset(
[
self.opc.SETUP_EXCEPT,
self.opc.SETUP_FINALLY,
# New in 2.7
self.opc.SETUP_WITH,
]
)
# opcodes that store values into a variable
self.designator_ops = frozenset([
self.opc.STORE_FAST, self.opc.STORE_NAME,
self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR,
self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2,
self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE,
self.opc.JUMP_ABSOLUTE
])
self.designator_ops = frozenset(
[
self.opc.STORE_FAST,
self.opc.STORE_NAME,
self.opc.STORE_GLOBAL,
self.opc.STORE_DEREF,
self.opc.STORE_ATTR,
self.opc.STORE_SLICE_0,
self.opc.STORE_SLICE_1,
self.opc.STORE_SLICE_2,
self.opc.STORE_SLICE_3,
self.opc.STORE_SUBSCR,
self.opc.UNPACK_SEQUENCE,
self.opc.JUMP_ABSOLUTE,
]
)
self.pop_jump_if_or_pop = frozenset([self.opc.JUMP_IF_FALSE_OR_POP,
self.opc.JUMP_IF_TRUE_OR_POP])
self.pop_jump_if_or_pop = frozenset(
[self.opc.JUMP_IF_FALSE_OR_POP, self.opc.JUMP_IF_TRUE_OR_POP]
)
return
pass
if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION == 2.7:
from xdis.version_info import PYTHON_VERSION_TRIPLE
if PYTHON_VERSION_TRIPLE[:2] == (2, 7):
import inspect
co = inspect.currentframe().f_code
tokens, customize = Scanner27().ingest(co)
for t in tokens:
print(t)
pass
else:
print("Need to be Python 2.7 to demo; I am %s." %
PYTHON_VERSION)
print("Need to be Python 2.7 to demo; I am %s." % ".".join(str(v) for v in PYTHON_VERSION_TRIPLE))

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2019 by Rocky Bernstein
# Copyright (c) 2015-2019, 2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
@ -66,7 +66,7 @@ class Scanner3(Scanner):
# at the their targets.
# Some blocks and END_ statements. And they can start
# a new statement
if self.version < 3.8:
if self.version < (3, 8):
setup_ops = [
self.opc.SETUP_LOOP,
self.opc.SETUP_EXCEPT,
@ -79,11 +79,11 @@ class Scanner3(Scanner):
setup_ops = [self.opc.SETUP_FINALLY]
self.setup_ops_no_loop = frozenset(setup_ops)
if self.version >= 3.2:
if self.version >= (3, 2):
setup_ops.append(self.opc.SETUP_WITH)
self.setup_ops = frozenset(setup_ops)
if self.version == 3.0:
if self.version[:2] == (3, 0):
self.pop_jump_tf = frozenset(
[self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE]
)
@ -114,7 +114,7 @@ class Scanner3(Scanner):
self.opc.JUMP_ABSOLUTE,
]
if self.version < 3.8:
if self.version < (3, 8):
statement_opcodes += [self.opc.BREAK_LOOP, self.opc.CONTINUE_LOOP]
self.statement_opcodes = frozenset(statement_opcodes) | self.setup_ops_no_loop
@ -135,7 +135,7 @@ class Scanner3(Scanner):
]
)
if self.version > 3.0:
if self.version > (3, 0):
self.jump_if_pop = frozenset(
[self.opc.JUMP_IF_FALSE_OR_POP, self.opc.JUMP_IF_TRUE_OR_POP]
)
@ -182,9 +182,9 @@ class Scanner3(Scanner):
]
)
if is_pypy or self.version >= 3.7:
if is_pypy or self.version >= (3, 7):
varargs_ops.add(self.opc.CALL_METHOD)
if self.version >= 3.5:
if self.version >= (3, 5):
varargs_ops |= set(
[
self.opc.BUILD_SET_UNPACK,
@ -193,7 +193,7 @@ class Scanner3(Scanner):
self.opc.BUILD_TUPLE_UNPACK,
]
)
if self.version >= 3.6:
if self.version >= (3, 6):
varargs_ops.add(self.opc.BUILD_CONST_KEY_MAP)
# Below is in bit order, "default = bit 0, closure = bit 3
self.MAKE_FUNCTION_FLAGS = tuple(
@ -257,7 +257,7 @@ class Scanner3(Scanner):
# If we have a JUMP_FORWARD after the
# RAISE_VARARGS then we have a "raise" statement
# else we have an "assert" statement.
if self.version == 3.0:
if self.version[:2] == (3, 0):
# Like 2.6, 3.0 doesn't have POP_JUMP_IF... so we have
# to go through more machinations
assert_can_follow = inst.opname == "POP_TOP" and i + 1 < n
@ -390,7 +390,7 @@ class Scanner3(Scanner):
pattr = const
pass
elif opname in ("MAKE_FUNCTION", "MAKE_CLOSURE"):
if self.version >= 3.6:
if self.version >= (3, 6):
# 3.6+ doesn't have MAKE_CLOSURE, so opname == 'MAKE_FUNCTION'
flags = argval
opname = "MAKE_FUNCTION_%d" % (flags)
@ -444,7 +444,7 @@ class Scanner3(Scanner):
# as JUMP_IF_NOT_DEBUG. The value is not used in these cases, so we put
# in arbitrary value 0.
customize[opname] = 0
elif self.version >= 3.6 and argval > 255:
elif self.version >= (3, 6) and argval > 255:
opname = "CALL_FUNCTION_KW"
pass
@ -483,7 +483,7 @@ class Scanner3(Scanner):
and self.insts[i + 1].opname == "JUMP_FORWARD"
)
if (self.version == 3.0 and self.insts[i + 1].opname == "JUMP_FORWARD"
if (self.version[:2] == (3, 0) and self.insts[i + 1].opname == "JUMP_FORWARD"
and not is_continue):
target_prev = self.offset2inst_index[self.prev_op[target]]
is_continue = (
@ -585,7 +585,7 @@ class Scanner3(Scanner):
if inst.has_arg:
label = self.fixed_jumps.get(offset)
oparg = inst.arg
if self.version >= 3.6 and self.code[offset] == self.opc.EXTENDED_ARG:
if self.version >= (3, 6) and self.code[offset] == self.opc.EXTENDED_ARG:
j = xdis.next_offset(op, self.opc, offset)
next_offset = xdis.next_offset(op, self.opc, j)
else:
@ -732,7 +732,7 @@ class Scanner3(Scanner):
end = current_end
parent = struct
if self.version < 3.8 and op == self.opc.SETUP_LOOP:
if self.version < (3, 8) and op == self.opc.SETUP_LOOP:
# We categorize loop types: 'for', 'while', 'while 1' with
# possibly suffixes '-loop' and '-else'
# Try to find the jump_back instruction of the loop.
@ -863,7 +863,7 @@ class Scanner3(Scanner):
# In some cases the pretarget can be a jump to the next instruction
# and these aren't and/or's either. We limit to 3.5+ since we experienced there
# but it might be earlier versions, or might be a general principle.
if self.version < 3.5 or pretarget.argval != target:
if self.version < (3, 5) or pretarget.argval != target:
# FIXME: this is not accurate The commented out below
# is what it should be. However grammar rules right now
# assume the incorrect offsets.
@ -957,7 +957,7 @@ class Scanner3(Scanner):
)
):
pass
elif self.version <= 3.2:
elif self.version <= (3, 2):
fix = None
jump_ifs = self.inst_matches(
start,
@ -976,7 +976,7 @@ class Scanner3(Scanner):
self.fixed_jumps[offset] = fix or match[-1]
return
else:
if self.version < 3.6:
if self.version < (3, 6):
# FIXME: this is putting in COME_FROMs in the wrong place.
# Fix up grammar so we don't need to do this.
# See cf_for_iter use in parser36.py
@ -1044,20 +1044,20 @@ class Scanner3(Scanner):
# if the condition jump is to a forward location.
# Also the existence of a jump to the instruction after "END_FINALLY"
# will distinguish "try/else" from "try".
if self.version < 3.8:
if self.version < (3, 8):
rtarget_break = (self.opc.RETURN_VALUE, self.opc.BREAK_LOOP)
else:
rtarget_break = (self.opc.RETURN_VALUE,)
if self.is_jump_forward(pre_rtarget) or (
rtarget_is_ja and self.version >= 3.5
rtarget_is_ja and self.version >= (3, 5)
):
if_end = self.get_target(pre_rtarget)
# If the jump target is back, we are looping
if (
if_end < pre_rtarget
and self.version < 3.8
and self.version < (3, 8)
and (code[prev_op[if_end]] == self.opc.SETUP_LOOP)
):
if if_end > start:
@ -1094,11 +1094,11 @@ class Scanner3(Scanner):
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
if self.opc.cmp_op[code[jump_prev + 1]] == "exception-match":
return
if self.version >= 3.5:
if self.version >= (3, 5):
# Python 3.5 may remove as dead code a JUMP
# instruction after a RETURN_VALUE. So we check
# based on seeing SETUP_EXCEPT various places.
if self.version < 3.6 and code[rtarget] == self.opc.SETUP_EXCEPT:
if self.version < (3, 6) and code[rtarget] == self.opc.SETUP_EXCEPT:
return
# Check that next instruction after pops and jump is
# not from SETUP_EXCEPT
@ -1111,14 +1111,14 @@ class Scanner3(Scanner):
for try_op in targets[next_op]:
come_from_op = code[try_op]
if (
self.version < 3.8
self.version < (3, 8)
and come_from_op == self.opc.SETUP_EXCEPT
):
return
pass
pass
if self.version >= 3.4:
if self.version >= (3, 4):
self.fixed_jumps[offset] = rtarget
if code[pre_rtarget] == self.opc.RETURN_VALUE:
@ -1137,8 +1137,8 @@ class Scanner3(Scanner):
# FIXME: this is very convoluted and based on rather hacky
# empirical evidence. It should go a way when
# we have better control-flow analysis
normal_jump = self.version >= 3.6
if self.version == 3.5:
normal_jump = self.version >= (3, 6)
if self.version[:2] == (3, 5):
j = self.offset2inst_index[target]
if j + 2 < len(self.insts) and self.insts[j + 2].is_jump_target:
normal_jump = self.insts[j + 1].opname == "POP_BLOCK"
@ -1155,7 +1155,7 @@ class Scanner3(Scanner):
if rtarget > offset:
self.fixed_jumps[offset] = rtarget
elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT:
elif self.version < (3, 8) and op == self.opc.SETUP_EXCEPT:
target = self.get_target(offset)
end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end
@ -1188,7 +1188,7 @@ class Scanner3(Scanner):
self.fixed_jumps[offset] = self.restrict_to_parent(target, parent)
pass
pass
elif self.version >= 3.5:
elif self.version >= (3, 5):
# 3.5+ has Jump optimization which too often causes RETURN_VALUE to get
# misclassified as RETURN_END_IF. Handle that here.
# In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF
@ -1278,7 +1278,7 @@ class Scanner3(Scanner):
start, end, instr, target, include_beyond_target
)
# Get all POP_JUMP_IF_TRUE (or) offsets
if self.version == 3.0:
if self.version[:2] == (3, 0):
jump_true_op = self.opc.JUMP_IF_TRUE
else:
jump_true_op = self.opc.POP_JUMP_IF_TRUE
@ -1295,17 +1295,16 @@ class Scanner3(Scanner):
if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION
from xdis.version_info import PYTHON_VERSION_TRIPLE
if PYTHON_VERSION >= 3.2:
if PYTHON_VERSION_TRIPLE >= (3, 2):
import inspect
co = inspect.currentframe().f_code
from uncompyle6 import PYTHON_VERSION
tokens, customize = Scanner3(PYTHON_VERSION).ingest(co)
tokens, customize = Scanner3(PYTHON_VERSION_TRIPLE).ingest(co)
for t in tokens:
print(t)
else:
print("Need to be Python 3.2 or greater to demo; I am %s." % PYTHON_VERSION)
print("Need to be Python 3.2 or greater to demo; I am %s." % sys.version)
pass

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017, 2020 by Rocky Bernstein
# Copyright (c) 2016-2017, 2020-2021 by Rocky Bernstein
"""
Python 3.0 bytecode scanner/deparser
@ -20,7 +20,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
class Scanner30(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.0, show_asm, is_pypy)
Scanner3.__init__(self, (3, 0), show_asm, is_pypy)
return
pass
@ -473,7 +473,7 @@ class Scanner30(Scanner3):
if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION == 3.0:
if PYTHON_VERSION == (3, 0):
import inspect
co = inspect.currentframe().f_code

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 by Rocky Bernstein
# Copyright (c) 2016-2017, 2021 by Rocky Bernstein
"""
Python 3.1 bytecode scanner/deparser
@ -16,13 +16,13 @@ from uncompyle6.scanners.scanner3 import Scanner3
class Scanner31(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.1, show_asm, is_pypy)
Scanner3.__init__(self, (3, 1), show_asm, is_pypy)
return
pass
if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION == 3.1:
if PYTHON_VERSION == (3, 1):
import inspect
co = inspect.currentframe().f_code
tokens, customize = Scanner31().ingest(co)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2017, 2021 by Rocky Bernstein
"""
Python 3.2 bytecode decompiler scanner.
@ -19,7 +19,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
class Scanner32(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.2, show_asm, is_pypy)
Scanner3.__init__(self, (3, 2), show_asm, is_pypy)
return
pass

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2019 by Rocky Bernstein
# Copyright (c) 2015-2019, 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -29,7 +29,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
class Scanner33(Scanner3):
def __init__(self, show_asm=False, is_pypy=False):
Scanner3.__init__(self, 3.3, show_asm)
Scanner3.__init__(self, (3, 3), show_asm)
return
pass

View File

@ -34,7 +34,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
class Scanner34(Scanner3):
def __init__(self, show_asm=None):
Scanner3.__init__(self, 3.4, show_asm)
Scanner3.__init__(self, (3, 4), show_asm)
return
pass

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017 by Rocky Bernstein
# Copyright (c) 2017, 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ JUMP_OPS = opc.JUMP_OPS
class Scanner35(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.5, show_asm, is_pypy)
Scanner3.__init__(self, (3, 5), show_asm, is_pypy)
return
pass

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2018 by Rocky Bernstein
# Copyright (c) 2016-2018, 2021 by Rocky Bernstein
"""
Python 3.6 bytecode decompiler scanner
@ -20,12 +20,12 @@ JUMP_OPS = opc.JUMP_OPS
class Scanner36(Scanner3):
def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.6, show_asm, is_pypy)
Scanner3.__init__(self, (3, 6), show_asm, is_pypy)
return
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
tokens, customize = Scanner3.ingest(self, co, classname, code_objects, show_asm)
not_pypy36 = not (self.version == 3.6 and self.is_pypy)
not_pypy36 = not (self.version[:2] == (3, 6) and self.is_pypy)
for t in tokens:
# The lowest bit of flags indicates whether the
# var-keyword argument is placed at the top of the stack

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016-2019 by Rocky Bernstein
# Copyright (c) 2016-2019, 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ JUMP_OPs = opc.JUMP_OPS
class Scanner37(Scanner37Base):
def __init__(self, show_asm=None):
Scanner37Base.__init__(self, 3.7, show_asm)
Scanner37Base.__init__(self, (3, 7), show_asm)
return
pass

View File

@ -55,7 +55,7 @@ class Scanner37Base(Scanner):
# Ops that start SETUP_ ... We will COME_FROM with these names
# Some blocks and END_ statements. And they can start
# a new statement
if self.version < 3.8:
if self.version < (3, 8):
setup_ops = [
self.opc.SETUP_LOOP,
self.opc.SETUP_EXCEPT,
@ -468,7 +468,7 @@ class Scanner37Base(Scanner):
and self.insts[i + 1].opname == "JUMP_FORWARD"
)
if self.version < 3.8 and (
if self.version < (3, 8) and (
is_continue
or (
inst.offset in self.stmts
@ -712,7 +712,7 @@ class Scanner37Base(Scanner):
end = current_end
parent = struct
if self.version < 3.8 and op == self.opc.SETUP_LOOP:
if self.version < (3, 8) and op == self.opc.SETUP_LOOP:
# We categorize loop types: 'for', 'while', 'while 1' with
# possibly suffixes '-loop' and '-else'
# Try to find the jump_back instruction of the loop.
@ -820,7 +820,7 @@ class Scanner37Base(Scanner):
target = inst.argval
self.fixed_jumps[offset] = target
elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT:
elif self.version < (3, 8) and op == self.opc.SETUP_EXCEPT:
target = self.get_target(offset)
end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2020 by Rocky Bernstein
# Copyright (c) 2019-2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -35,7 +35,7 @@ JUMP_OPs = opc.JUMP_OPS
class Scanner38(Scanner37):
def __init__(self, show_asm=None):
Scanner37Base.__init__(self, 3.8, show_asm)
Scanner37Base.__init__(self, (3, 8), show_asm)
self.debug = False
return
@ -61,7 +61,7 @@ class Scanner38(Scanner37):
if self.debug and jump_back_targets:
print(jump_back_targets)
loop_ends = []
next_end = tokens[len(tokens)-1].off2int() + 10
next_end = tokens[len(tokens) - 1].off2int() + 10
for i, token in enumerate(tokens):
opname = token.kind
offset = token.offset
@ -70,13 +70,19 @@ class Scanner38(Scanner37):
if self.debug:
print("%sremove loop offset %s" % (" " * len(loop_ends), offset))
pass
next_end = loop_ends[-1] if len(loop_ends) else tokens[len(tokens)-1].off2int() + 10
next_end = (
loop_ends[-1]
if len(loop_ends)
else tokens[len(tokens) - 1].off2int() + 10
)
if offset in jump_back_targets:
next_end = off2int(jump_back_targets[offset], prefer_last=False)
if self.debug:
print("%sadding loop offset %s ending at %s" %
(' ' * len(loop_ends), offset, next_end))
print(
"%sadding loop offset %s ending at %s"
% (" " * len(loop_ends), offset, next_end)
)
loop_ends.append(next_end)
# Turn JUMP opcodes into "BREAK_LOOP" opcodes.
@ -107,10 +113,7 @@ class Scanner38(Scanner37):
jump_back_token = tokens[jump_back_index]
# Is this a forward jump not next to a JUMP_BACK ? ...
break_loop = (
token.linestart
and jump_back_token != "JUMP_BACK"
)
break_loop = token.linestart and jump_back_token != "JUMP_BACK"
# or if there is looping jump back, then that loop
# should start before where the "break" instruction sits.
@ -136,5 +139,4 @@ if __name__ == "__main__":
print(t.format())
pass
else:
print("Need to be Python 3.8 to demo; I am %s." %
PYTHON_VERSION)
print("Need to be Python 3.8 to demo; I am %s." % PYTHON_VERSION)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019, 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ JUMP_OPs = opc.JUMP_OPS
class Scanner39(Scanner38):
def __init__(self, show_asm=None):
Scanner37Base.__init__(self, 3.9, show_asm)
Scanner37Base.__init__(self, (3, 9), show_asm)
return
pass

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2019 by Rocky Bernstein
# Copyright (c) 2018-2019, 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -60,8 +60,8 @@ def customize_for_version(self, is_pypy, version):
"assign3": ("%|%c, %c, %c = %c, %c, %c\n", 5, 6, 7, 0, 1, 2),
"try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
})
if version >= 3.0:
if version >= 3.2:
if version >= (3, 0):
if version >= (3, 2):
TABLE_DIRECT.update(
{"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)}
)
@ -72,20 +72,20 @@ def customize_for_version(self, is_pypy, version):
TABLE_DIRECT.update(
{"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))}
)
if version <= 2.6:
if version <= (2, 6):
TABLE_DIRECT["testtrue_then"] = TABLE_DIRECT["testtrue"]
if 2.4 <= version <= 2.6:
if (2, 4) <= version <= (2, 6):
TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)})
else:
TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)})
if version >= 2.5:
if version >= (2, 5):
from uncompyle6.semantics.customize25 import customize_for_version25
customize_for_version25(self, version)
if version >= 2.6:
if version >= (2, 6):
from uncompyle6.semantics.customize26_27 import (
customize_for_version26_27,
)
@ -137,7 +137,7 @@ def customize_for_version(self, is_pypy, version):
),
}
)
if version == 2.4:
if version == (2, 4):
def n_iftrue_stmt24(node):
self.template_engine(("%c", 0), node)
self.default(node)
@ -146,7 +146,7 @@ def customize_for_version(self, is_pypy, version):
self.n_iftrue_stmt24 = n_iftrue_stmt24
else: # version <= 2.3:
TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)})
if version <= 2.1:
if version <= (2, 1):
TABLE_DIRECT.update(
{
"importmultiple": ("%c", 2),

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein
# Copyright (c) 2019 2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -28,7 +28,7 @@ def customize_for_version26_27(self, version):
# For 2.6 we use the older syntax which
# matches how we parse this in bytecode
########################################
if version > 2.6:
if version > (2, 6):
TABLE_DIRECT.update({
'except_cond2': ( '%|except %c as %c:\n', 1, 5 ),
# When a generator is a single parameter of a function,

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2020 by Rocky Bernstein
# Copyright (c) 2018-2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -58,7 +58,7 @@ def customize_for_version3(self, version):
}
)
assert version >= 3.0
assert version >= (3, 0)
# In 2.5+ and 3.0+ "except" handlers and the "finally" can appear in one
# "try" statement. So the below has the effect of combining the
@ -107,7 +107,7 @@ def customize_for_version3(self, version):
collections = [node[-3]]
list_ifs = []
if self.version == 3.0 and n != "list_iter":
if self.version[:2] == (3, 0) and n != "list_iter":
# FIXME 3.0 is a snowflake here. We need
# special code for this. Not sure if this is totally
# correct.
@ -204,10 +204,10 @@ def customize_for_version3(self, version):
self.listcomp_closure3 = listcomp_closure3
def n_classdef3(node):
"""Handle "classdef" nonterminal for 3.0 >= version 3.0 <= 3.5
"""Handle "classdef" nonterminal for 3.0 >= version 3.0 < 3.6
"""
assert 3.0 <= self.version <= 3.5
assert (3, 0) <= self.version < (3, 6)
# class definition ('class X(A,B,C):')
cclass = self.currentclass
@ -220,7 +220,7 @@ def customize_for_version3(self, version):
# * subclass_code - the code for the subclass body
subclass_info = None
if node == "classdefdeco2":
if self.version <= 3.3:
if self.version < (3, 4):
class_name = node[2][0].attr
else:
class_name = node[1][2].attr
@ -233,7 +233,7 @@ def customize_for_version3(self, version):
assert "mkfunc" == build_class[1]
mkfunc = build_class[1]
if mkfunc[0] in ("kwargs", "no_kwargs"):
if 3.0 <= self.version <= 3.2:
if (3, 0) <= self.version < (3, 3):
for n in mkfunc:
if hasattr(n, "attr") and iscode(n.attr):
subclass_code = n.attr
@ -355,7 +355,7 @@ def customize_for_version3(self, version):
self.n_yield_from = n_yield_from
if 3.2 <= version <= 3.4:
if (3, 2) <= version <= (3, 4):
def n_call(node):
@ -398,7 +398,7 @@ def customize_for_version3(self, version):
self.default(node)
self.n_call = n_call
elif version < 3.2:
elif version < (3, 2):
def n_call(node):
mapping = self._get_mapping(node)
@ -416,9 +416,9 @@ def customize_for_version3(self, version):
# Handling EXTENDED_ARG before MAKE_FUNCTION ...
i = -1 if node[-2] == "EXTENDED_ARG" else 0
if self.version <= 3.2:
if self.version < (3, 3):
code = node[-2 + i]
elif self.version >= 3.3 or node[-2] == "kwargs":
elif self.version >= (3, 3) or node[-2] == "kwargs":
# LOAD_CONST code object ..
# LOAD_CONST 'x0' if >= 3.3
# EXTENDED_ARG
@ -468,7 +468,7 @@ def customize_for_version3(self, version):
"LOAD_CLASSDEREF": ("%{pattr}",),
}
)
if version >= 3.4:
if version >= (3, 4):
#######################
# Python 3.4+ Changes #
#######################
@ -478,13 +478,13 @@ def customize_for_version3(self, version):
"yield_from": ("yield from %c", (0, "expr")),
}
)
if version >= 3.5:
if version >= (3, 5):
customize_for_version35(self, version)
if version >= 3.6:
if version >= (3, 6):
customize_for_version36(self, version)
if version >= 3.7:
if version >= (3, 7):
customize_for_version37(self, version)
if version >= 3.8:
if version >= (3, 8):
customize_for_version38(self, version)
pass # version >= 3.8
pass # 3.7

View File

@ -78,7 +78,7 @@ def find_globals_and_nonlocals(node, globs, nonlocals, code, version):
code, version)
elif n.kind in read_global_ops:
globs.add(n.pattr)
elif (version >= 3.0
elif (version >= (3, 0)
and n.kind in nonglobal_ops
and n.pattr in code.co_freevars
and n.pattr != code.co_name
@ -149,7 +149,7 @@ def print_docstring(self, indent, docstring):
# Must be unicode in Python2
self.write('u')
docstring = repr(docstring.expandtabs())[2:-1]
elif PYTHON3 and 2.4 <= self.version <= 2.7:
elif PYTHON3 and (2, 4) <= self.version[:2] <= (2, 7):
try:
repr(docstring.expandtabs())[1:-1].encode("ascii")
except UnicodeEncodeError:

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 by Rocky Bernstein
# Copyright (c) 2015-2021 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
# This program is free software: you can redistribute it and/or modify
@ -192,7 +192,7 @@ def make_function2(self, node, is_lambda, nested=1, code_node=None):
)
# Python 2 doesn't support the "nonlocal" statement
assert self.version >= 3.0 or not nonlocals
assert self.version >= (3, 0) or not nonlocals
for g in sorted((all_globals & self.mod_globs) | globals):
self.println(self.indent, "global ", g)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 by Rocky Bernstein
# Copyright (c) 2015-2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -366,9 +366,9 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
# Python 3.3+ adds a qualified name at TOS (-1)
# moving down the LOAD_LAMBDA instruction
if 3.0 <= self.version <= 3.2:
if (3, 0) <= self.version <= (3, 2):
lambda_index = -2
elif 3.03 <= self.version:
elif (3, 3) <= self.version:
lambda_index = -3
else:
lambda_index = None
@ -590,7 +590,7 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
ends_in_comma = True
kw_args = [None] * kwonlyargcount
if self.version <= 3.3:
if self.version <= (3, 3):
kw_nodes = node[0]
else:
kw_nodes = node[args_node.attr[0]]

View File

@ -483,7 +483,7 @@ class SourceWalker(GenericASTTraversal, object):
and node[0][0][0] == "LOAD_CONST"
and node[0][0][0].pattr is None
)
if self.version <= 2.6:
if self.version <= (2, 6):
return ret
else:
# FIXME: should the SyntaxTree expression be folded into
@ -537,7 +537,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_yield(self, node):
if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
self.template_engine(("yield %c", 0), node)
elif self.version <= 2.4:
elif self.version <= (2, 4):
# Early versions of Python don't allow a plain "yield"
self.write("yield None")
else:
@ -823,7 +823,7 @@ class SourceWalker(GenericASTTraversal, object):
self.prune()
def n_alias(self, node):
if self.version <= 2.1:
if self.version <= (2, 1):
if len(node) == 2:
store = node[1]
assert store == "store"
@ -848,10 +848,10 @@ class SourceWalker(GenericASTTraversal, object):
def n_import_from(self, node):
relative_path_index = 0
if self.version >= 2.5:
if self.version >= (2, 5):
if node[relative_path_index].pattr > 0:
node[2].pattr = ("." * node[relative_path_index].pattr) + node[2].pattr
if self.version > 2.7:
if self.version > (2, 7):
if isinstance(node[1].pattr, tuple):
imports = node[1].pattr
for pattr in imports:
@ -882,11 +882,11 @@ class SourceWalker(GenericASTTraversal, object):
# Python changes make function this much that we need at least 3 different routines,
# and probably more in the future.
def make_function(self, node, is_lambda, nested=1, code_node=None, annotate=None):
if self.version <= 2.7:
if self.version <= (2, 7):
make_function2(self, node, is_lambda, nested, code_node)
elif 3.0 <= self.version <= 3.5:
elif (3, 0) <= self.version <= (3, 5):
make_function3(self, node, is_lambda, nested, code_node)
elif self.version >= 3.6:
elif self.version >= (3, 6):
make_function36(self, node, is_lambda, nested, code_node)
def n_docstring(self, node):
@ -978,7 +978,7 @@ class SourceWalker(GenericASTTraversal, object):
"""List comprehensions"""
p = self.prec
self.prec = 100
if self.version >= 2.7:
if self.version >= (2, 7):
if self.is_pypy:
self.n_list_comp_pypy27(node)
return
@ -1005,7 +1005,7 @@ class SourceWalker(GenericASTTraversal, object):
assert n == "lc_body"
self.write("[ ")
if self.version >= 2.7:
if self.version >= (2, 7):
expr = n[0]
list_iter = node[-1]
else:
@ -1090,15 +1090,15 @@ class SourceWalker(GenericASTTraversal, object):
self.prec = 27
# FIXME: clean this up
if self.version >= 3.0 and node == "dict_comp":
if self.version >= (3, 0) and node == "dict_comp":
cn = node[1]
elif self.version <= 2.7 and node == "generator_exp":
elif self.version <= (2, 7) and node == "generator_exp":
if node[0] == "LOAD_GENEXPR":
cn = node[0]
elif node[0] == "load_closure":
cn = node[1]
elif self.version >= 3.0 and node in ("generator_exp", "generator_exp_async"):
elif self.version >= (3, 0) and node in ("generator_exp", "generator_exp_async"):
if node[0] == "load_genexpr":
load_genexpr = node[0]
elif node[1] == "load_genexpr":
@ -1167,9 +1167,9 @@ class SourceWalker(GenericASTTraversal, object):
def n_generator_exp(self, node):
self.write("(")
iter_index = 3
if self.version > 3.2:
if self.version > (3, 2):
code_index = -6
if self.version > 3.6:
if self.version > (3, 6):
# Python 3.7+ adds optional "come_froms" at node[0]
iter_index = 4
else:
@ -1184,7 +1184,7 @@ class SourceWalker(GenericASTTraversal, object):
self.write("{")
if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
self.comprehension_walk_newer(node, 1, 0)
elif node[0].kind == "load_closure" and self.version >= 3.0:
elif node[0].kind == "load_closure" and self.version >= (3, 0):
self.setcomprehension_walk3(node, collection_index=4)
else:
self.comprehension_walk(node, iter_index=4)
@ -1241,7 +1241,7 @@ class SourceWalker(GenericASTTraversal, object):
pass
pass
elif ast in ("dict_comp", "set_comp"):
assert self.version == 3.0
assert self.version == (3, 0)
for k in ast:
if k in ("dict_comp_header", "set_comp_header"):
n = k
@ -1313,7 +1313,7 @@ class SourceWalker(GenericASTTraversal, object):
# Python 2.7+ starts including set_comp_body
# Python 3.5+ starts including set_comp_func
# Python 3.0 is yet another snowflake
if self.version != 3.0 and self.version < 3.7:
if self.version != (3, 0) and self.version < (3, 7):
assert n.kind in (
"lc_body",
"list_if37",
@ -1356,7 +1356,7 @@ class SourceWalker(GenericASTTraversal, object):
self.preorder(node[in_node_index])
# Here is where we handle nested list iterations.
if ast == "list_comp" and self.version != 3.0:
if ast == "list_comp" and self.version != (3, 0):
list_iter = ast[1]
assert list_iter == "list_iter"
if list_iter[0] == "list_for":
@ -1379,7 +1379,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_listcomp(self, node):
self.write("[")
if node[0].kind == "load_closure":
assert self.version >= 3.0
assert self.version >= (3, 0)
self.listcomp_closure3(node)
else:
if node == "listcomp_async":
@ -1447,9 +1447,9 @@ class SourceWalker(GenericASTTraversal, object):
def n_classdef(self, node):
if self.version >= 3.6:
if self.version >= (3, 6):
self.n_classdef36(node)
elif self.version >= 3.0:
elif self.version >= (3, 0):
self.n_classdef3(node)
# class definition ('class X(A,B,C):')
@ -1514,7 +1514,7 @@ class SourceWalker(GenericASTTraversal, object):
return
n_subclasses = len(node[:-1])
if n_subclasses > 0 or self.version > 2.4:
if n_subclasses > 0 or self.version > (2, 4):
# Not an old-style pre-2.2 class
self.write("(")
@ -1525,7 +1525,7 @@ class SourceWalker(GenericASTTraversal, object):
self.write(sep, value)
sep = line_separator
if n_subclasses > 0 or self.version > 2.4:
if n_subclasses > 0 or self.version > (2, 4):
# Not an old-style pre-2.2 class
self.write(")")
@ -1674,7 +1674,7 @@ class SourceWalker(GenericASTTraversal, object):
self.write("{")
line_number = self.line_number
if self.version >= 3.0 and not self.is_pypy:
if self.version >= (3, 0) and not self.is_pypy:
if node[0].kind.startswith("kvlist"):
# Python 3.5+ style key/value list in dict
kv_node = node[0]
@ -1760,14 +1760,14 @@ class SourceWalker(GenericASTTraversal, object):
self.write(sep[1:])
pass
elif node[0].kind.startswith("dict_entry"):
assert self.version >= 3.5
assert self.version >= (3, 5)
template = ("%C", (0, len(node[0]), ", **"))
self.template_engine(template, node[0])
sep = ""
elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
-1
].kind.startswith("dict_entry"):
assert self.version >= 3.5
assert self.version >= (3, 5)
# FIXME: I think we can intermingle dict_comp's with other
# dictionary kinds of things. The most common though is
# a sequence of dict_comp's
@ -1790,7 +1790,7 @@ class SourceWalker(GenericASTTraversal, object):
else:
sep = ""
opname = node[-1].kind
if self.is_pypy and self.version >= 3.5:
if self.is_pypy and self.version >= (3, 5):
if opname.startswith("BUILD_CONST_KEY_MAP"):
keys = node[-2].attr
# FIXME: DRY this and the above
@ -1966,7 +1966,7 @@ class SourceWalker(GenericASTTraversal, object):
# In Python 2.4, unpack is used in (a, b, c) of:
# except RuntimeError, (a, b, c):
if self.version < 2.7:
if self.version < (2, 7):
node.kind = "unpack_w_parens"
self.default(node)
@ -1984,7 +1984,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_assign(self, node):
# A horrible hack for Python 3.0 .. 3.2
if 3.0 <= self.version <= 3.2 and len(node) == 2:
if (3, 0) <= self.version <= (3, 2) and len(node) == 2:
if (
node[0][0] == "LOAD_FAST"
and node[0][0].pattr == "__locals__"
@ -2197,7 +2197,7 @@ class SourceWalker(GenericASTTraversal, object):
if k.startswith("CALL_METHOD"):
# This happens in PyPy and Python 3.7+
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
elif self.version >= 3.6 and k.startswith("CALL_FUNCTION_KW"):
elif self.version >= (3, 6) and k.startswith("CALL_FUNCTION_KW"):
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
elif op == "CALL_FUNCTION":
TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", PRECEDENCE["yield"]-1))
@ -2219,12 +2219,12 @@ class SourceWalker(GenericASTTraversal, object):
if op == "CALL_FUNCTION_VAR":
# Python 3.5 only puts optional args (the VAR part)
# lowest down the stack
if self.version == 3.5:
if self.version == (3, 5):
if str == "%c(%C, ":
entry = ("%c(*%C, %c)", 0, p2, -2)
elif str == "%c(%C":
entry = ("%c(*%C)", 0, (1, 100, ""))
elif self.version == 3.4:
elif self.version == (3, 4):
# CALL_FUNCTION_VAR's top element of the stack contains
# the variable argument list
if v == 0:
@ -2244,7 +2244,7 @@ class SourceWalker(GenericASTTraversal, object):
# Python 3.5 only puts optional args (the VAR part)
# lowest down the stack
na = v & 0xFF # positional parameters
if self.version == 3.5 and na == 0:
if self.version == (3, 5) and na == 0:
if p2[2]:
p2 = (2, -2, ", ")
entry = (str, 0, p2, 1, -2)
@ -2326,7 +2326,7 @@ class SourceWalker(GenericASTTraversal, object):
del ast[0]
first_stmt = ast[0]
if 3.0 <= self.version <= 3.3:
if (3, 0) <= self.version <= (3, 3):
try:
if first_stmt == "store_locals":
if self.hide_internal:
@ -2352,7 +2352,7 @@ class SourceWalker(GenericASTTraversal, object):
ast[0] = ast[0][0]
first_stmt = ast[0]
if self.version < 3.0:
if self.version < (3, 0):
# Should we ditch this in favor of the "else" case?
qualname = ".".join(self.classes)
QUAL_NAME = SyntaxTree(
@ -2612,7 +2612,7 @@ def code_deparse(
assert not nonlocals
if version >= 3.0:
if version >= (3, 0):
load_op = "LOAD_STR"
else:
load_op = "LOAD_CONST"

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2020 by Rocky Bernstein
# Copyright (c) 2019-2021 by Rocky Bernstein
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -48,7 +48,7 @@ def is_docstring(node, version, co_consts):
# return node.kind == "assign" and node[1][0].pattr == "__doc__"
# except:
# return False
if version <= 2.7:
if version <= (2, 7):
doc_load = "LOAD_CONST"
else:
doc_load = "LOAD_STR"
@ -110,7 +110,7 @@ class TreeTransform(GenericASTTraversal, object):
than the code field is seen and used.
"""
if self.version >= 3.7:
if self.version >= (3, 7):
code_index = -3
else:
code_index = -2