Compare commits

...

27 Commits
3.7.0 ... 3.7.2

Author SHA1 Message Date
rocky
b21e8b8b57 Get ready for release 3.7.2 2020-06-27 23:08:46 -04:00
rocky
4007b8b702 Back off "or" check using instructions vs opcodes 2020-06-27 11:44:23 -04:00
rocky
598b58796d Back off buggy "or" check 2020-06-27 11:33:46 -04:00
rocky
f7bad891a4 Last commit fixed test_pep352.py 2020-06-27 11:22:53 -04:00
rocky
357f28dd89 Add "comp_if_not" for 2.6- 2020-06-27 11:16:47 -04:00
rocky
5cc572147a Handle more ifelse reduction rules patterns 2020-06-27 09:10:48 -04:00
rocky
11be90758f Workaround bug detecting MAKE_FUNCTION docstrings 2020-06-26 07:17:31 -04:00
rocky
e3720515ae Adjust for newer xdis 2020-06-21 20:20:25 -04:00
rocky
7dec354a47 Merge branch 'master' of github.com:rocky/python-uncompyle6 2020-06-17 10:15:07 -04:00
rocky
2a8daca25d Fix broken __doc__ transform yet again...
Hopefully by using first_child() we have something more robust now.
2020-06-17 10:12:56 -04:00
rocky
7799819cad Add another 3.7 stdlib exclusion test 2020-06-17 05:42:10 -04:00
rocky
c6c50b5dfb Disable compile-farm 3.8.3 checking 2020-06-17 05:29:04 -04:00
rocky
d357898bbf Towards fixing a 3.8 try except-as bug 2020-06-15 06:03:28 -04:00
rocky
c4e7ddf90a Administrivia 2020-06-12 21:29:32 -04:00
rocky
02f502c40a New grammar rule often imples expanded reduce rule 2020-06-12 21:12:02 -04:00
rocky
de4fbb08f2 Get ready for release 3.7.1 2020-06-12 20:20:58 -04:00
rocky
e14675c2dc Handle 3.7+ "else" branch removal...
As seen in _cmp() of python3.8/distutils/version.py with optimization -O2
2020-06-12 13:18:33 -04:00
rocky
3449be024b CI take 3. 2020-06-10 22:18:28 -04:00
rocky
8b50b15f0a CI update take 2 2020-06-10 22:17:13 -04:00
rocky
e2e925679d Update CI to use git xdis 2020-06-10 22:15:55 -04:00
rocky
7deeee8502 Push "with" grammar improvements back to 3.6 2020-06-04 05:53:21 -04:00
rocky
acdd025162 ast-check "for" is a loop; sync "withas" test ..
with decompyle3.
2020-06-04 05:34:19 -04:00
rocky
9acb3cf068 Fix bug in 3.8 with .. as 2020-06-04 05:24:22 -04:00
rocky
40a653cd3b Bump min xdis version...
it fixes a bug in stdlib testing
2020-05-31 03:17:09 -04:00
rocky
3ac3979535 With a newer xdis, some stdlib test work now 2020-05-31 03:10:52 -04:00
rocky
7eba933cfa More excludes 2020-05-24 21:25:07 -04:00
rocky
ad5d3333da A regression regarding "and"/"or" with "continue" 2020-05-19 10:20:08 -04:00
40 changed files with 302 additions and 103 deletions

View File

@@ -36,9 +36,11 @@ jobs:
# fallback to using the latest cache if no exact match is found
- v2-dependencies-
- run: pip install --user --upgrade setuptools
- run: pip install --user -e .
- run: pip install --user -r requirements-dev.txt
- run:
command: | # Use pip to install dependengcies
pip install --user --upgrade setuptools
pip install --user -e .
pip install --user -r requirements-dev.txt
# Save dependency cache
- save_cache:

18
NEWS.md
View File

@@ -1,3 +1,21 @@
3.7.2: 2020-6-27
================
* Use newer xdis
* Docstrings (again) which were broken again on earlier Python
* Fix 2.6 and 2.7 decompilation bug in handling "list if" comprehensions
3.7.1: 2020-6-12 Fleetwood66
====================================================
Released to pick up new xdis version which has fixes to read bytestings better on 3.x
* Handle 3.7+ "else" branch removal adAs seen in `_cmp()` of `python3.8/distutils/version.py` with optimization `-O2`
* 3.6+ "with" and "with .. as" grammar improvements
* ast-check for "for" loop was missing some grammar rules
3.7.0: 2020-5-19 Primidi 1st Prairial - Alfalfa - HF
====================================================

View File

@@ -69,7 +69,7 @@ entry_points = {
]}
ftp_url = None
install_requires = ["spark-parser >= 1.8.9, < 1.9.0",
"xdis >= 4.6.0, < 4.7.0"]
"xdis >= 4.7.0, <5.1.0"]
license = "GPL3"
mailing_list = "python-debugger@googlegroups.com"

View File

@@ -2,17 +2,17 @@
**Table of Contents**
- [Get latest sources:](#get-latest-sources)
- [Change version in uncompyle6/version.py](#change-version-in-uncompyle6versionpy)
- [Change version in uncompyle6/version.py:](#change-version-in-uncompyle6versionpy)
- [Update ChangeLog:](#update-changelog)
- [Update NEWS from ChangeLog:](#update-news-from-changelog)
- [Update NEWS.md from ChangeLog:](#update-newsmd-from-changelog)
- [Make sure pyenv is running and check newer versions](#make-sure-pyenv-is-running-and-check-newer-versions)
- [Switch to python-2.4, sync that up and build that first since it creates a tarball which we don't want.](#switch-to-python-24-sync-that-up-and-build-that-first-since-it-creates-a-tarball-which-we-dont-want)
- [Update NEWS from master branch](#update-news-from-master-branch)
- [Check against all versions](#check-against-all-versions)
- [Check against older versions](#check-against-older-versions)
- [Make packages and tag](#make-packages-and-tag)
- [Upload single package and look at Rst Formating](#upload-single-package-and-look-at-rst-formating)
- [Upload rest of versions](#upload-rest-of-versions)
- [Push tags:](#push-tags)
- [Check package on github](#check-package-on-github)
- [Release on Github](#release-on-github)
- [Get onto PyPI](#get-onto-pypi)
- [Update tags:](#update-tags)
<!-- markdown-toc end -->
# Get latest sources:
@@ -60,27 +60,36 @@
$ . ./admin-tools/make-dist-newer.sh
$ twine check dist/uncompyle6-$VERSION*
# Upload single package and look at Rst Formating
# Check package on github
$ twine check dist/uncompyle6-${VERSION}*
$ twine upload dist/uncompyle6-${VERSION}-py3.3.egg
$ mkdir /tmp/gittest; pushd /tmp/gittest
$ pyenv local 3.7.5
$ pip install -e git://github.com/rocky/python-uncompyle6.git#egg=uncompyle6
$ uncompyle6 --help
$ pip uninstall uncompyle6
$ popd
# Upload rest of versions
$ twine upload dist/uncompyle6-${VERSION}*
# Release on Github
Goto https://github.com/rocky/python-uncompyle6/releases
# Push tags:
Now check the *tagged* release. (Checking the untagged release was previously done).
Todo: turn this into a script in `admin-tools`
$ pushd /tmp/gittest
$ pip install -e git://github.com/rocky/python-uncompyle6.git@$VERSION#egg=uncompyle6
$ uncompyle6 --help
$ pip uninstall uncompyle6
$ popd
# Get onto PyPI
$ twine upload dist/uncompyle6-${VERSION}*
# Update tags:
$ git push --tags
# Check on a VM
$ cd /virtual/vagrant/virtual/vagrant/ubuntu-zesty
$ vagrant up
$ vagrant ssh
$ pyenv local 3.5.2
$ pip install --upgrade uncompyle6
$ exit
$ vagrant halt
$ git pull --tags

View File

@@ -19,6 +19,7 @@ for path in py_source:
else:
cfile = "bytecode_%s%s/%s" % (version, suffix, short) + "c"
print("byte-compiling %s to %s" % (path, cfile))
py_compile.compile(path, cfile)
optimize = 2
py_compile.compile(path, cfile, optimize=optimize)
if isinstance(version, str) or version >= (2, 6, 0):
os.system("../bin/uncompyle6 -a -T %s" % cfile)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -51,7 +51,7 @@ for VERSION in $PYVERSIONS ; do
LOGFILE=/tmp/${MAIN}-$VERSION-$$.log
case "$VERSION" in
3.7.7 | 3.8.2 | 3.1.5 | 3.0.1 )
3.7.7 | 3.8.3 | 3.1.5 | 3.0.1 )
continue
;;
3.5.9 )

View File

@@ -2,6 +2,7 @@
# Bug was code not knowing which Python versions
# have kwargs coming before positional args in code.
"""This program is self-checking!"""
# RUNNABLE!
def tometadata(self, metadata, schema, Table, args, name=None):

View File

@@ -0,0 +1,12 @@
# From python3.8/distutils/version.py with optimization -O2
# The bug was that the other "else" constant propagated removed.
# NOTE: this program needs to be compile with optimization
def _cmp (b, c):
if b:
if c:
return 0
else:
return 1
else:
assert False, "never get here"

View File

@@ -16,3 +16,25 @@ def withas_bug(self, nested, a, b):
with self.assertRaises(ZeroDivisionError):
with nested(a(), b()) as (x, y):
1 // 0
# From 3.7.7 test_functools.py
# Bug is a unreachable code after "return"
def test_invalid_registrations(x):
return
with x:
x = 1
# From 3.7.7 test_re.py
# Bug was hooking in c_with.
def test_re_tests(tests):
for t in tests:
with a:
continue
# Adapted from 3.8 distutils/command/config.py
# In 3.8 the problem was in handling "with .. as" code
def _gen_temp_sourcefile(x, a, headers, lang):
with x as y:
if a:
y = 2
return 5

View File

@@ -55,7 +55,6 @@ SKIP_TESTS=(
[test_ossaudiodev.py]=1 # it fails on its own
[test_pdb.py]=1 # Line-number specific
[test_pep277.py]=1 # it fails on its own
[test_pep352.py]=1 # Investigate
[test_plistlib.py]=1 # it fails on its own
[test_pwd.py]=1 # Long test - might work? Control flow?
[test_pyclbr.py]=1 # Investigate

View File

@@ -60,7 +60,6 @@ SKIP_TESTS=(
[test_ossaudiodev.py]=1 # it fails on its own
[test_pep277.py]=1 # it fails on its own
[test_pep352.py]=1 # Investigate
[test_pyclbr.py]=1 # Investigate
[test_pwd.py]=1 # Long test - might work? Control flow?
[test_py3kwarn.py]=1 # it fails on its own

View File

@@ -17,8 +17,6 @@ SKIP_TESTS=(
[test_peepholer.py]=1
[test_pep352.py]=1
[test_quopri.py]=1 # TypeError: Can't convert 'bytes' object to str implicitly
[test_runpy.py]=1
[test_ssl.py]=1 # too installation specific
@@ -35,5 +33,4 @@ if (( BATCH )) ; then
# Fails in crontab environment?
# Figure out what's up here
SKIP_TESTS[test_exception_variations.py]=1
SKIP_TESTS[test_quopri.py]=1
fi

View File

@@ -1,4 +1,15 @@
SKIP_TESTS=(
# FIXME: Did this work sometime in the past ?
# for elem in g(s):
# if not tgt and isOdd(elem): continue
# is erroneously:
# for elem in g(s):
# if tgt or isOdd(elem):
# pass
# else:
# tgt.append(elem)
[test_itertools.py]=1
[test_buffer.py]=1 # FIXME: Works on c90ff51
[test_cmath.py]=1 # FIXME: Works on c90ff51

View File

@@ -1,4 +1,15 @@
SKIP_TESTS=(
# FIXME: Did this work sometime in the past ?
# for elem in g(s):
# if not tgt and isOdd(elem): continue
# is erroneously:
# for elem in g(s):
# if tgt or isOdd(elem):
# pass
# else:
# tgt.append(elem)
[test_itertools.py]=1
[test_buffer.py]=1 # FIXME: Works on c90ff51
[test_cmath.py]=1 # FIXME: Works on c90ff51
[test_strftime.py]=1 # FIXME: Works on c90ff51
@@ -87,5 +98,4 @@ if (( batch )) ; then
# Figure out what's up here
SKIP_TESTS[test_exception_variations.py]=1
SKIP_TESTS[test_mailbox.py]=1 # Takes to long on POWER; over 15 secs
SKIP_TESTS[test_quopri.py]=1
fi

View File

@@ -138,7 +138,6 @@ if (( BATCH )) ; then
SKIP_TESTS[test_ioctl.py]=1 # it fails on its own
SKIP_TESTS[test_poplib.py]=1 # May be a result of POWER installation
SKIP_TESTS[test_quopri.py]=1
SKIP_TESTS[test_sysconfig.py]=1 # POWER extension fails
SKIP_TESTS[test_tarfile.py]=1 # too long to run on POWER 15 secs
SKIP_TESTS[test_venv.py]=1 # takes too long 11 seconds

View File

@@ -123,8 +123,6 @@ SKIP_TESTS=(
[test_pyclbr.py]=1 # it fails on its own
[test_pydoc.py]=1 # it fails on its own
[test_quopri.py]=1 # AssertionError: b'123=four' != '123=four'
[test_random.py]=1 # it fails on its own
[test_range.py]=1
[test_regrtest.py]=1 # test takes too long to run: 12 seconds

View File

@@ -1,4 +1,19 @@
SKIP_TESTS=(
# FIXME: Did this work sometime in the past ?
# for elem in g(s):
# if not tgt and isOdd(elem): continue
# is erroneously:
# for elem in g(s):
# if tgt or isOdd(elem):
# pass
# else:
# tgt.append(elem)
[test_itertools.py]=1
# Fails on decompyle3 as well.
# complicated control flow and "and/or" expressions
[test_pickle.py]=1
[test_builtin.py]=1 # FIXME works on decompyle6
[test_context.py]=1 # FIXME works on decompyle6
[test_doctest2.py]=1 # FIXME works on decompyle6
@@ -48,6 +63,7 @@ SKIP_TESTS=(
[test_faulthandler.py]=1 # test takes too long before decompiling
[test_fileinput.py]=1 # Test assertion failures
[test_finalization.py]=1 # if/else logic
[test_frame.py]=1 # test assertion errors
[test_ftplib.py]=1 # parse error
[test_fstring.py]=1 # need to disambiguate leading fstrings from docstrings

View File

@@ -78,7 +78,6 @@ case $PYVERSION in
# Fails in crontab environment?
# Figure out what's up here
SKIP_TESTS[test_exception_variations.py]=1
SKIP_TESTS[test_quopri.py]=1
fi
;;
3.1)
@@ -91,7 +90,6 @@ case $PYVERSION in
# Fails in crontab environment?
# Figure out what's up here
SKIP_TESTS[test_exception_variations.py]=1
SKIP_TESTS[test_quopri.py]=1
fi
;;
3.2)

View File

@@ -119,12 +119,10 @@ def decompile(
mapstream = _get_outstream(mapstream)
deparsed = deparse_code_with_map(
bytecode_version,
co,
out,
showasm,
showast,
showgrammar,
bytecode_version,
debug_opts,
code_objects=code_objects,
is_pypy=is_pypy,
)

View File

@@ -232,7 +232,10 @@ class Python26Parser(Python2Parser):
comp_for ::= SETUP_LOOP expr for_iter store comp_iter jb_pb_come_from
comp_body ::= gen_comp_body
comp_iter ::= comp_if_not
comp_if_not ::= expr jmp_true comp_iter
comp_body ::= gen_comp_body
for_block ::= l_stmts_opt _come_froms POP_TOP JUMP_BACK

View File

@@ -8,7 +8,6 @@ from uncompyle6.parser import PythonParserSingle, nop_func
from uncompyle6.parsers.parse2 import Python2Parser
from uncompyle6.parsers.reducecheck import (
or_check,
ifelsestmt,
tryelsestmt,
)

View File

@@ -679,6 +679,7 @@ class Python3Parser(PythonParser):
"RAISE",
"SETUP",
"UNPACK",
"WITH",
)
)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2019 Rocky Bernstein
# Copyright (c) 2016-2020 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
@@ -305,28 +305,25 @@ class Python36Parser(Python35Parser):
self.addRule(rule, nop_func)
# Check to combine assignment + annotation into one statement
self.check_reduce['assign'] = 'token'
elif opname == "WITH_CLEANUP_START":
rules_str = """
stmt ::= with_null
with_null ::= with_suffix
with_suffix ::= WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
"""
self.addRule(rules_str, nop_func)
elif opname == 'SETUP_WITH':
rules_str = """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt COME_FROM_WITH
with_suffix
# Removes POP_BLOCK LOAD_CONST from 3.6-
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
# Removes POP_BLOCK LOAD_CONST from 3.6-
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
with_suffix
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_WITH
with_suffix
"""
if self.version < 3.8:
rules_str += """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
LOAD_CONST
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
"""
else:
rules_str += """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH
END_FINALLY
"""
self.addRule(rules_str, nop_func)
pass
pass

View File

@@ -94,6 +94,9 @@ class Python37Parser(Python37BaseParser):
else_suitec ::= c_stmts
else_suitec ::= returns
else_suite_opt ::= else_suite
else_suite_opt ::= pass
stmt ::= classdef
stmt ::= call_stmt
@@ -635,6 +638,12 @@ class Python37Parser(Python37BaseParser):
if_exp37 ::= expr expr jf_cfs expr COME_FROM
jf_cfs ::= JUMP_FORWARD _come_froms
ifelsestmt ::= testexpr c_stmts_opt jf_cfs else_suite opt_come_from_except
# This is probably more realistically an "ifstmt" (with a null else)
# see _cmp() of python3.8/distutils/__pycache__/version.cpython-38.opt-1.pyc
ifelsestmt ::= testexpr stmts jf_cfs else_suite_opt opt_come_from_except
expr_pjit ::= expr POP_JUMP_IF_TRUE
expr_jit ::= expr JUMP_IF_TRUE
expr_jt ::= expr jmp_true

View File

@@ -127,6 +127,7 @@ class Python37BaseParser(PythonParser):
"RAISE",
"SETUP",
"UNPACK",
"WITH",
)
)
@@ -993,55 +994,70 @@ class Python37BaseParser(PythonParser):
)
custom_ops_processed.add(opname)
elif opname == "WITH_CLEANUP_START":
rules_str = """
stmt ::= with_null
with_null ::= with_suffix
with_suffix ::= WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
"""
self.addRule(rules_str, nop_func)
elif opname == "SETUP_WITH":
rules_str = """
stmt ::= with
stmt ::= withasstmt
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with ::= expr
SETUP_WITH POP_TOP
suite_stmts_opt
COME_FROM_WITH
with_suffix
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with_suffix
with ::= expr
SETUP_WITH POP_TOP
suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
with_suffix
withasstmt ::= expr
SETUP_WITH store suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
with_suffix
with ::= expr
SETUP_WITH POP_TOP suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with_suffix
withasstmt ::= expr
SETUP_WITH store suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with ::= expr
SETUP_WITH POP_TOP suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
withasstmt ::= expr
SETUP_WITH store suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with_suffix
"""
if self.version < 3.8:
rules_str += """
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
LOAD_CONST
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with_suffix
"""
else:
rules_str += """
with ::= expr
with ::= expr
SETUP_WITH POP_TOP suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
with_suffix
withasstmt ::= expr
SETUP_WITH store suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_WITH
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
withasstmt ::= expr
SETUP_WITH store suite_stmts
POP_BLOCK BEGIN_FINALLY COME_FROM_WITH with_suffix
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_WITH
WITH_CLEANUP_START WITH_CLEANUP_FINISH
END_FINALLY
with_suffix
"""
self.addRule(rules_str, nop_func)

View File

@@ -45,6 +45,7 @@ class Python38Parser(Python37Parser):
stmt ::= try_elsestmtl38
stmt ::= try_except_ret38
stmt ::= try_except38
stmt ::= try_except_as
stmt ::= whilestmt38
stmt ::= whileTruestmt38
stmt ::= call_stmt
@@ -133,6 +134,8 @@ class Python38Parser(Python37Parser):
except_cond1 ::= DUP_TOP expr COMPARE_OP jmp_false
POP_TOP POP_TOP POP_TOP
POP_EXCEPT
except_cond_as ::= DUP_TOP expr COMPARE_OP POP_JUMP_IF_FALSE
POP_TOP STORE_FAST POP_TOP
try_elsestmtl38 ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
except_handler38 COME_FROM
@@ -145,6 +148,10 @@ class Python38Parser(Python37Parser):
# suite_stmts has a return
try_except38 ::= SETUP_FINALLY POP_BLOCK suite_stmts
except_handler38b
try_except_as ::= SETUP_FINALLY POP_BLOCK suite_stmts
except_handler_as END_FINALLY COME_FROM
try_except_as ::= SETUP_FINALLY suite_stmts
except_handler_as END_FINALLY COME_FROM
try_except_ret38 ::= SETUP_FINALLY returns except_ret38a
try_except_ret38a ::= SETUP_FINALLY returns except_handler38c
@@ -165,6 +172,11 @@ class Python38Parser(Python37Parser):
except_handler38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
POP_EXCEPT POP_TOP stmts END_FINALLY
except_handler38c ::= COME_FROM_FINALLY except_cond1a except_stmts
POP_EXCEPT JUMP_FORWARD COME_FROM
except_handler_as ::= COME_FROM_FINALLY except_cond_as tryfinallystmt
POP_EXCEPT JUMP_FORWARD COME_FROM
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_FINALLY suite_stmts_opt
END_FINALLY

View File

@@ -95,6 +95,34 @@ IFELSE_STMT_RULES = frozenset(
"else_suite",
),
),
(
"ifelsestmt",
(
"testexpr",
"stmts",
"jf_cfs",
"else_suite_opt",
"opt_come_from_except",
),
),
(
"ifelsestmt",
(
"testexpr",
"stmts",
"jf_cfs",
"\\e_else_suite_opt",
"\\e_opt_come_from_except")
),
(
"ifelsestmt",
(
"testexpr",
"stmts",
"jf_cfs",
"\\e_else_suite_opt",
"opt_come_from_except")
),
])
def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
@@ -103,12 +131,17 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
# ifelsestmt jumped outside of loop. No good.
return True
# print("XXX", first, last)
# for t in range(first, last):
# print(tokens[t])
# print("=" * 30)
if rule not in IFELSE_STMT_RULES:
# print("XXX", rule)
return False
# Avoid if/else where the "then" is a "raise_stmt1" for an
# assert statemetn. Parse this as an "assert" instead.
# assert statement. Parse this as an "assert" instead.
stmts = ast[1]
if stmts in ("c_stmts",) and len(stmts) == 1:
raise_stmt1 = stmts[0]
@@ -176,9 +209,7 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
if jump_else_end == "jf_cf_pop":
jump_else_end = jump_else_end[0]
jump_to_jump = False
if jump_else_end == "JUMP_FORWARD":
jump_to_jump = True
endif_target = int(jump_else_end.pattr)
last_offset = tokens[last].off2int()
if endif_target != last_offset:

View File

@@ -8,22 +8,29 @@ FIXME idea: extend parsing system to do same kinds of checks or nonterminal
before reduction and don't reduce when there is a problem.
"""
def checker(ast, in_loop, errors):
if ast is None:
return
in_loop = (in_loop or (ast.kind in ('while1stmt', 'whileTruestmt',
'whilestmt', 'whileelsestmt', 'while1elsestmt',
'for_block'))
or ast.kind.startswith('async_for'))
if ast.kind in ('aug_assign1', 'aug_assign2') and ast[0][0] == 'and':
in_loop = (
in_loop
or ast.kind.startswith("for")
or ast.kind.startswith("while")
or ast.kind.startswith("async_for")
)
if ast.kind in ("aug_assign1", "aug_assign2") and ast[0][0] == "and":
text = str(ast)
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'
error_text = (
"\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t"
+ "\n# ".join(text.split("\n"))
+ "\n"
)
errors.append(error_text)
for node in ast:
if not in_loop and node.kind in ('continue', 'break'):
if not in_loop and node.kind in ("continue", "break"):
text = str(node)
error_text = '\n# not in loop:\n#\t' + '\n# '.join(text.split("\n"))
error_text = "\n# not in loop:\n#\t" + "\n# ".join(text.split("\n"))
errors.append(error_text)
if hasattr(node, '__repr1__'):
if hasattr(node, "__repr1__"):
checker(node, in_loop, errors)

View File

@@ -50,12 +50,24 @@ def customize_for_version38(self, version):
"%|%c\n", 0
),
"except_cond_as": (
"%|except %c as %c:\n",
(1, "expr"),
(-2, "STORE_FAST"),
),
'except_handler38': (
'%c', (2, 'except_stmts') ),
'except_handler38a': (
'%c', (-2, 'stmts') ),
"except_handler_as": (
"%c%+\n%+%c%-",
(1, "except_cond_as"),
(2, "tryfinallystmt"),
),
'except_ret38a': (
'return %c', (4, 'expr') ),
@@ -105,6 +117,13 @@ def customize_for_version38(self, version):
'try_except38': (
'%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
(-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ),
"try_except_as": (
"%|try:\n%+%c%-\n%|%-%c\n\n",
(-4, "suite_stmts"), # Go from the end because of POP_BLOCK variation
(-3, "except_handler_as"),
),
"try_except_ret38": (
"%|try:\n%+%c%-\n%|except:\n%+%|%c%-\n\n",
(1, "returns"),

View File

@@ -895,6 +895,12 @@ class SourceWalker(GenericASTTraversal, object):
doc_node = node[0]
if doc_node.attr:
docstring = doc_node.attr
if not isinstance(docstring, str):
# FIXME: we have mistakenly tagged something as a doc
# string in transform when it isn't one.
# The rule in n_mkfunc is pretty flaky.
self.prune()
return
else:
docstring = node[0].pattr

View File

@@ -95,10 +95,18 @@ class TreeTransform(GenericASTTraversal, object):
code = find_code_node(node, code_index).attr
mkfunc_pattr = node[-1].pattr
if isinstance(mkfunc_pattr, tuple):
assert len(mkfunc_pattr, 4) and isinstance(mkfunc_pattr, int)
is_closure = node[-1].pattr[3] != 0
else:
# FIXME: This is what we had before. It is hoaky and probably wrong.
is_closure = mkfunc_pattr == "closure"
if (
node[-1].pattr != "closure"
(not is_closure)
and len(code.co_consts) > 0
and code.co_consts[0] is not None
and isinstance(code.co_consts[0], str)
):
docstring_node = SyntaxTree(
"docstring", [Token("LOAD_STR", has_arg=True, pattr=code.co_consts[0])]
@@ -433,6 +441,7 @@ class TreeTransform(GenericASTTraversal, object):
ast[i] = ast[i][0]
if is_docstring(self.ast[i]):
load_const = self.ast[i].first_child()
docstring_ast = SyntaxTree(
"docstring",
[
@@ -440,8 +449,8 @@ class TreeTransform(GenericASTTraversal, object):
"LOAD_STR",
has_arg=True,
offset=0,
attr=self.ast[i][0][0].attr,
pattr=self.ast[i][0][0].pattr,
attr=load_const.attr,
pattr=load_const.pattr,
)
],
)

View File

@@ -12,4 +12,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This file is suitable for sourcing inside POSIX shell as
# well as importing into Python
VERSION="3.7.0" # noqa
VERSION="3.7.2" # noqa