Compare commits

..

45 Commits

Author SHA1 Message Date
rocky
8fe6309650 Get ready for release 3.3.3 2019-05-19 16:55:52 -04:00
R. Bernstein
4c4aa393df Update HISTORY.md 2019-05-17 10:24:13 -04:00
R. Bernstein
a8b8c2908c Update README.rst 2019-05-15 03:25:06 -04:00
R. Bernstein
cb406e2581 Update README.rst 2019-05-15 03:24:33 -04:00
R. Bernstein
20b16c44ff Update README.rst 2019-05-15 03:22:05 -04:00
rocky
3abe8d11d3 3.7 handling of 4-level attribute import
Fixes #239
2019-05-14 12:09:38 -04:00
R. Bernstein
26140934da Merge pull request #237 from rocky/for-iter-come-from
Less dishonest COME_FROMs in 3.6+ code
2019-05-14 09:31:27 -04:00
x0ret
b62752eca1 2 more 3.7 chained comparison rule 2019-05-14 17:51:51 +04:30
rocky
9db446d928 Another 3.7 chained comparison rule 2019-05-14 07:26:18 -04:00
rocky
46acb74745 Only add forward-jumping COME_FROM in 3.6+
Is this a repeat commit?
2019-05-14 06:28:29 -04:00
rocky
44e1288e2f Less dishonest COME_FROMs
Addresses all of the problems seen in 3.7 datetime.py.

However we limit COME_FROMs only to forward jumps, not back (which in the case of Python code right now means looping) jumps.
2019-05-13 23:15:55 -04:00
R. Bernstein
ce9270dda0 Merge pull request #236 from x0ret/master
Python 3.7 decompiling full 3.7.3 library without error #235
2019-05-13 11:53:07 -04:00
x0ret
3d732db3cc fix chained compare parse error 2019-05-13 19:57:24 +04:30
x0ret
009a74da7d fix UnboundLocalError 2019-05-13 19:41:42 +04:30
R. Bernstein
251eb6da1b Merge pull request #233 from rocky/fstring
Revise format string handling
2019-05-13 09:43:47 -04:00
rocky
8b5e0f49f8 Handle {{ }} escape, but when appropriate 2019-05-13 09:41:16 -04:00
rocky
1cc08d9598 Make precedence table top-bottom order reference...
in https://docs.python.org/2/reference/expressions.html#operator-precedence or
https://docs.python.org/3/reference/expressions.html#operator-precedence
.
2019-05-13 09:41:16 -04:00
x0ret
d99e78d46d set precedences value for format strings 2019-05-13 09:41:09 -04:00
rocky
b94cce7b12 Revise format string handling
fstring_single{1,2} -> format_value{1,2} to match Python AST names
better
2019-05-13 09:40:32 -04:00
R. Bernstein
fe786b2b95 Merge pull request #232 from x0ret/master
handling LOAD_CONST in build_tuple and multiple call_ex_kw(s) issues
2019-05-12 06:11:11 -04:00
x0ret
bf56fbeeec enhance call_ex_kw(s) positional args handling 2019-05-12 13:11:31 +04:30
rocky
6d8d9fd83b Go over precedence in calls 2019-05-11 23:32:24 -04:00
rocky
78ca6a0c1f Add 3.7 testcase for ex_kw call 2019-05-11 19:37:44 -04:00
rocky
86dd321256 Accept x0ret's suggestion for 3.6+ if detection..
in the presense of a try block.

Fixes #229
2019-05-10 19:36:16 -04:00
rocky
4db364f701 And another tweak. 2019-05-10 17:29:03 -04:00
rocky
c03b039714 Small tweaks to last commit 2019-05-10 17:25:25 -04:00
R. Bernstein
d97509495e Merge pull request #230 from x0ret/master
implements n_call_ex_kw as discussed in #227
2019-05-10 16:57:44 -04:00
x0ret
4d793ba1b2 implements n_call_ex_kw as discussed in #227 2019-05-10 23:55:47 +04:30
R. Bernstein
590d2f44f1 Update README.rst 2019-05-10 09:29:47 -04:00
rocky
e875b79a75 Fix 3.6. call_ex_kw semantic action
Was missing positional args parameter in template. Fix submited by @x0ret

Fixes #227
2019-05-09 09:27:10 -04:00
rocky
b57ca392a2 2.7 confusion around "and" vs comprehension "if"
Fixes #225
2019-05-08 16:27:41 -04:00
rocky
a132e2ace6 Better 3.6+ async detection 2019-05-08 13:50:57 -04:00
rocky
b05500dd49 More 3.6+ fstring bugs 2019-05-08 08:57:30 -04:00
rocky
65307f257c Administrivia 2019-05-08 06:06:53 -04:00
rocky
8909fe8d37 Merge branch 'master' of github.com:rocky/python-uncompyle6 2019-05-08 06:04:11 -04:00
rocky
733a44e22f Revise and hopefully improve 3.6+ fstring handling 2019-05-08 06:03:39 -04:00
rocky
f2f17740ee 2.7 if_expr_true restriction ...
condition_true -> if_expr_true
condition_lambda -> if_expr_lambda

These correspond to the Python AST names better.
2019-05-05 16:09:10 -04:00
rocky
393e5c9303 IfExp precidence handling in 2.6...
2.7 still has a bug
2019-05-05 09:48:20 -04:00
rocky
8c611476fe ifexpr -> if_expr (to track better AST camelcase) 2019-05-05 08:20:21 -04:00
rocky
6df65a87bc Fix precidence between list_if and if_expr in 3.x 2019-05-05 08:16:29 -04:00
rocky
bb94c7f5bc Ned custom 3.7+ IfExp rules 2019-05-04 22:57:06 -04:00
rocky
8e9ce0be31 3.7: if <expr> and not <expr> else <expr> 2019-05-04 22:14:07 -04:00
rocky
bc49469704 delete_subscr -> delete_subscript ...
to better (but not exactly) match the Python AST
2019-05-04 19:43:00 -04:00
rocky
5905cce1de Python 3.7 ifelse handling 2019-05-04 19:38:37 -04:00
rocky
af816c9e60 Administrivia 2019-05-03 23:25:25 -04:00
142 changed files with 1190 additions and 681 deletions

View File

@@ -42,7 +42,7 @@ jobs:
# This is based on your 1.0 configuration file or project settings
- run:
working_directory: ~/rocky/python-uncompyle6
command: pyenv install 2.4.6 && pyenv local 2.4.6 && pyenv rehash && easy_install nose && pyenv rehash
command: pyenv local 2.7.11 && pyenv rehash && pip install virtualenv && pip install nose && pip install pep8 && pip install six && pyenv rehash
# Dependencies
# This would typically go in either a build or a build-and-test job when using workflows
# Restore the dependency cache
@@ -55,7 +55,9 @@ jobs:
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
- v1-dep-
# This is based on your 1.0 configuration file or project settings
- run: easy_install spark_parser==1.8.5 && easy_install xdis==3.8.4
- run: pip install --upgrade setuptools
- run: pip install -e .
- run: pip install pytest==3.2.5 hypothesis==3.0.0
# Save dependency cache
- save_cache:
key: v1-dep-{{ .Branch }}-{{ epoch }}
@@ -73,8 +75,8 @@ jobs:
# Test
# This would typically be a build job when using workflows, possibly combined with build
# This is based on your 1.0 configuration file or project settings
- run: python ./setup.py develop && make check-2.4
- run: cd ./test/stdlib && pyenv local 2.4.6 && bash ./runtests.sh 'test_[p-z]*.py'
- run: python ./setup.py develop && make check-2.7
- run: cd ./test/stdlib && pyenv local 2.7.11 && bash ./runtests.sh 'test_[p-z]*.py'
# Teardown
# If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each
# Save test results

View File

@@ -1,11 +1,15 @@
language: python
python:
- '2.7' # this is a cheat here because travis doesn't do 2.4-2.6
- '3.5'
- '2.7'
- '2.6'
- '3.4'
- '3.6'
matrix:
include:
- python: '2.7'
- python: '3.7'
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
install:

View File

@@ -115,7 +115,7 @@ mechanisms and addressed problems and extensions by some other means.
Specifically, in `uncompyle`, decompilation of python bytecode 2.5 &
2.6 is done by transforming the byte code into a pseudo-2.7 Python
bytecode and is based on code from Eloi Vanderbeken. A bit of this
could have bene easily added by modifying grammar rules.
could have been easily added by modifying grammar rules.
This project, `uncompyle6`, abandons that approach for various
reasons. Having a grammar per Python version is much cleaner and it

View File

@@ -40,9 +40,8 @@ check-3.0 check-3.1 check-3.2 check-3.6:
check-3.7: pytest
$(MAKE) -C test check
#:Tests for Python 2.4-2.5 (don't have pytest)
check-2.4 check-2.5:
$(MAKE) -C test $@
check-3.8:
$(MAKE) -C test check
#:PyPy 2.6.1 PyPy 5.0.1, or PyPy 5.8.0-beta0
# Skip for now

44
NEWS.md
View File

@@ -1,3 +1,31 @@
3.3.3 2019-05-19 Henry and Lewis
================================
As before, decomplation bugs fixed. The focus has primarily been on
Python 3.7. But with this release, releases will be put on hold,as a
better control-flow detection is worked on . Tis has been needed for a
while, and is long overdue. It will probably also take a while to get
done as good as what we have now.
However this work will be done in a new project
[decompyle3](https://github.com/rocky/python-decompile3). In contrast
to _uncompyle6_ the code wil be written assuming a modern Python 3,
e.g. 3.7. It is originally intended to decompile Python version 3.7
and greater.
* A number of Python 3.7+ chained comparisons were fixed
* Revise Python 3.6ish format string handling
* Go over operator precedence, e.g. for AST IfExp
Reported Bug Fixes
------------------
* [#239: 3.7 handling of 4-level attribute import](https://github.com/rocky/python-uncompyle6/issues/239),
* [#229: Inconsistent if block in python3.6](https://github.com/rocky/python-uncompyle6/issues/229),
* [#227: Args not appearing in decompiled src when kwargs is specified explicitly (call_ex_kw)](https://github.com/rocky/python-uncompyle6/issues/227)
2.7 confusion around "and" versus comprehension "if"
* [#225: 2.7 confusion around "and" vs comprehension "if"](https://github.com/rocky/python-uncompyle6/issues/225)
3.3.2 2019-05-03 Better Friday
==============================
@@ -12,7 +40,6 @@ get addressed in future releases
Pypy 3.6 support was started. Pypy 3.x detection fixed (via xdis)
3.3.1 2019-04-19 Good Friday
==========================
@@ -27,10 +54,11 @@ Lots of decomplation bugs, especially in the 3.x series fixed. Don't worry thoug
* Fix some parser failures fixes in 3.4+ using test_pyenvlib
* Add more run tests
3.3.0 2019-43-14 Holy Week
3.3.0 2019-04-14 Holy Week
==========================
* First cut at Python 3.8 (many bug remain)
* First cut at Python 3.8 (many bugs remain)
* Reinstate -c | --compile (compile before disassembly) option
* The usual smattering of bug and doc fixes
3.2.6 2019-03-23 Mueller Report
@@ -41,11 +69,13 @@ Mostly more of the same: bug fixes and pull requests.
Bug Fixes
-----------
* [#155: Python 3.x bytecode confusing "try/else" with "try" in a loop](https://github.com/rocky/python-uncompyle6/issues/155),
* [#200: Python 3 bug in not detecting end bounds of an "if" ... "elif"](https://github.com/rocky/python-uncompyle6/issues/200),
* [#208: Comma placement in 3.6 and 3.7 **kwargs](https://github.com/rocky/python-uncompyle6/issues/208),
* [#209: Fix "if" return boundary in 3.6+](https://github.com/rocky/python-uncompyle6/issues/209),
* [#221: Wrong grammar for nested ifelsestmt (in Python 3.7 at least)](https://github.com/rocky/python-uncompyle6/issues/221)
* [#215: 2.7 can have two JUMP_BACKs at the end of a while loop](https://github.com/rocky/python-uncompyle6/issues/215)
* [#209: Fix "if" return boundary in 3.6+](https://github.com/rocky/python-uncompyle6/issues/209),
* [#208: Comma placement in 3.6 and 3.7 **kwargs](https://github.com/rocky/python-uncompyle6/issues/208),
* [#200: Python 3 bug in not detecting end bounds of an "if" ... "elif"](https://github.com/rocky/python-uncompyle6/issues/200),
* [#155: Python 3.x bytecode confusing "try/else" with "try" in a loop](https://github.com/rocky/python-uncompyle6/issues/155),
Pull Requests
----------------

View File

@@ -152,7 +152,7 @@ for that bytecode version. Having done this the bytecode produced
could be compared with the original bytecode. However as Python's code
generation got better, this is no longer feasible.
There verification that we use that doesn't check bytecode for
The verification that we use that doesn't check bytecode for
equivalence but does check to see if the resulting decompiled source
is a valid Python program by running the Python interpreter. Because
the Python language has changed so much, for best results you should
@@ -194,8 +194,12 @@ Between Python 3.5, 3.6 and 3.7 there have been major changes to the
Currently not all Python magic numbers are supported. Specifically in
some versions of Python, notably Python 3.6, the magic number has
changes several times within a version. We support only the released
magic. There are also customized Python interpreters, notably Dropbox,
changes several times within a version.
**We support only released versions, not candidate versions.** Note however
that the magic of a released version is usually the same as the *last* candidate version prior to release.
There are also customized Python interpreters, notably Dropbox,
which use their own magic and encrypt bytcode. With the exception of
the Dropbox's old Python 2.5 interpreter this kind of thing is not
handled.
@@ -226,7 +230,7 @@ See Also
* https://github.com/rocky/python-uncompyle6/wiki : Wiki Documents which describe the code and aspects of it in more detail
.. _trepan: https://pypi.python.org/pypi/trepan2
.. _trepan: https://pypi.python.org/pypi/trepan2g
.. _compiler: https://pypi.python.org/pypi/spark_parser
.. _HISTORY: https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md
.. _debuggers: https://pypi.python.org/pypi/trepan3k

0
admin-tools/check-newer-versions.sh Normal file → Executable file
View File

0
admin-tools/check-older-versions.sh Normal file → Executable file
View File

View File

@@ -1,46 +0,0 @@
git pull
Change version in uncompyle6/version.py
source uncompyle6/version.py
echo $VERSION
git commit -m"Get ready for release $VERSION" .
Update ChangeLog:
make ChangeLog
Update NEWS from ChangeLog
make check
git commit --amend .
git push
Make sure pyenv is running
# Pyenv
source admin-tools/check-newer-versions.sh
# Switch to python-2.4 and build that first...
source admin-tools/setup-python-2.4
rm ChangeLog
git merge master
Update NEWS from master branch
git commit -m"Get ready for release $VERSION" .
source admin-tools/check-older-versions.sh
source admin-tools/check-newer-versions.sh
make-dist-older.sh
git tag release-python-2.4-$VERSION
./make-dist-newer.sh
git tag release-$VERSION
twine upload dist/uncompyle6-${VERSION}*

2
admin-tools/setup-master.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
PYTHON_VERSION=3.6.5
PYTHON_VERSION=3.6.8
# FIXME put some of the below in a common routine
function finish {

0
admin-tools/setup-python-2.4.sh Normal file → Executable file
View File

View File

@@ -1,3 +0,0 @@
#!/bin/bash
cd $(dirname ${BASH_SOURCE[0]})/..
git pull

View File

@@ -30,7 +30,7 @@ def list_comp():
[y for y in range(3)]
def get_parsed_for_fn(fn):
code = fn.func_code
code = fn.__code__ if PYTHON3 else fn.func_code
return deparse(code, version=PYTHON_VERSION)
def check_expect(expect, parsed, fn_name):

View File

@@ -10,7 +10,7 @@ else:
maxint = sys.maxint
from uncompyle6.semantics.helper import print_docstring
class PrintFake:
class PrintFake():
def __init__(self):
self.pending_newlines = 0
self.f = StringIO()

View File

@@ -20,7 +20,7 @@ def bug_loop(disassemble, tb=None):
disassemble(tb)
def test_if_in_for():
code = bug.func_code
code = bug.__code__
scan = get_scanner(PYTHON_VERSION)
if 2.7 <= PYTHON_VERSION <= 3.0 and not IS_PYPY:
scan.build_instructions(code)

158
pytest/test_fstring.py Normal file
View File

@@ -0,0 +1,158 @@
# std
# test
from uncompyle6 import PYTHON_VERSION, deparse_code
import pytest
pytestmark = pytest.mark.skipif(PYTHON_VERSION <= 2.6,
reason='hypothesis needs 2.7 or later')
if PYTHON_VERSION > 2.6:
import hypothesis
from hypothesis import strategies as st
# uncompyle6
@st.composite
def expressions(draw):
# todo : would be nice to generate expressions using hypothesis however
# this is pretty involved so for now just use a corpus of expressions
# from which to select.
return draw(st.sampled_from((
'abc',
'len(items)',
'x + 1',
'lineno',
'container',
'self.attribute',
'self.method()',
# These expressions are failing, I think these are control
# flow problems rather than problems with FORMAT_VALUE,
# however I need to confirm this...
#'sorted(items, key=lambda x: x.name)',
#'func(*args, **kwargs)',
#'text or default',
#'43 if life_the_universe and everything else None'
)))
@st.composite
def format_specifiers(draw):
"""
Generate a valid format specifier using the rules:
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= integer
precision ::= integer
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
See https://docs.python.org/2/library/string.html
:param draw: Let hypothesis draw from other strategies.
:return: An example format_specifier.
"""
alphabet_strategy = st.characters(min_codepoint=ord('a'), max_codepoint=ord('z'))
fill = draw(st.one_of(alphabet_strategy, st.none()))
align = draw(st.sampled_from(list('<>=^')))
fill_align = (fill + align or '') if fill else ''
type_ = draw(st.sampled_from('bcdeEfFgGnosxX%'))
can_have_sign = type_ in 'deEfFgGnoxX%'
can_have_comma = type_ in 'deEfFgG%'
can_have_precision = type_ in 'fFgG'
can_have_pound = type_ in 'boxX%'
can_have_zero = type_ in 'oxX'
sign = draw(st.sampled_from(list('+- ') + [''])) if can_have_sign else ''
pound = draw(st.sampled_from(('#', '',))) if can_have_pound else ''
zero = draw(st.sampled_from(('0', '',))) if can_have_zero else ''
int_strategy = st.integers(min_value=1, max_value=1000)
width = draw(st.one_of(int_strategy, st.none()))
width = str(width) if width is not None else ''
comma = draw(st.sampled_from((',', '',))) if can_have_comma else ''
if can_have_precision:
precision = draw(st.one_of(int_strategy, st.none()))
precision = '.' + str(precision) if precision else ''
else:
precision = ''
return ''.join((fill_align, sign, pound, zero, width, comma, precision, type_,))
@st.composite
def fstrings(draw):
"""
Generate a valid f-string.
See https://www.python.org/dev/peps/pep-0498/#specification
:param draw: Let hypothsis draw from other strategies.
:return: A valid f-string.
"""
character_strategy = st.characters(
blacklist_characters='\r\n\'\\s{}',
min_codepoint=1,
max_codepoint=1000,
)
is_raw = draw(st.booleans())
integer_strategy = st.integers(min_value=0, max_value=3)
expression_count = draw(integer_strategy)
content = []
for _ in range(expression_count):
expression = draw(expressions())
conversion = draw(st.sampled_from(('', '!s', '!r', '!a',)))
has_specifier = draw(st.booleans())
specifier = ':' + draw(format_specifiers()) if has_specifier else ''
content.append('{{{}{}}}'.format(expression, conversion, specifier))
content.append(draw(st.text(character_strategy)))
content = ''.join(content)
return "f{}'{}'".format('r' if is_raw else '', content)
@pytest.mark.skipif(PYTHON_VERSION != 3.6, reason='need Python 3.6')
@hypothesis.given(format_specifiers())
def test_format_specifiers(format_specifier):
"""Verify that format_specifiers generates valid specifiers"""
try:
exec('"{:' + format_specifier + '}".format(0)')
except ValueError as e:
if 'Unknown format code' not in str(e):
raise
def run_test(text):
hypothesis.assume(len(text))
hypothesis.assume("f'{" in text)
expr = text + '\n'
code = compile(expr, '<string>', 'single')
deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single')
recompiled = compile(deparsed.text, '<string>', 'single')
if recompiled != code:
print(recompiled)
print('================')
print(code)
print('----------------')
assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')'
@pytest.mark.skipif(PYTHON_VERSION != 3.6, reason='need Python 3.6')
@hypothesis.given(fstrings())
def test_uncompyle_fstring(fstring):
"""Verify uncompyling fstring bytecode"""
run_test(fstring)
@pytest.mark.skipif(PYTHON_VERSION != 3.6, reason='need Python 3.6+')
@pytest.mark.parametrize('fstring', [
"f'{abc}{abc!s}'",
"f'{abc}0'",
])
def test_uncompyle_direct(fstring):
"""useful for debugging"""
run_test(fstring)

View File

@@ -0,0 +1,185 @@
import string
from uncompyle6 import PYTHON_VERSION
import pytest
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
if PYTHON_VERSION > 2.6:
from hypothesis import given, assume, example, settings, strategies as st
from validate import validate_uncompyle
from test_fstring import expressions
alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits)
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
@st.composite
def function_calls(draw,
min_keyword_args=0, max_keyword_args=5,
min_positional_args=0, max_positional_args=5,
min_star_args=0, max_star_args=1,
min_double_star_args=0, max_double_star_args=1):
"""
Strategy factory for generating function calls.
:param draw: Callable which draws examples from other strategies.
:return: The function call text.
"""
st_positional_args = st.lists(
alpha,
min_size=min_positional_args,
max_size=max_positional_args
)
st_keyword_args = st.lists(
alpha,
min_size=min_keyword_args,
max_size=max_keyword_args
)
st_star_args = st.lists(
alpha,
min_size=min_star_args,
max_size=max_star_args
)
st_double_star_args = st.lists(
alpha,
min_size=min_double_star_args,
max_size=max_double_star_args
)
positional_args = draw(st_positional_args)
keyword_args = draw(st_keyword_args)
st_values = st.lists(
expressions(),
min_size=len(keyword_args),
max_size=len(keyword_args)
)
keyword_args = [
x + '=' + e
for x, e in
zip(keyword_args, draw(st_values))
]
star_args = ['*' + x for x in draw(st_star_args)]
double_star_args = ['**' + x for x in draw(st_double_star_args)]
arguments = positional_args + keyword_args + star_args + double_star_args
draw(st.randoms()).shuffle(arguments)
arguments = ','.join(arguments)
function_call = 'fn({arguments})'.format(arguments=arguments)
try:
# TODO: Figure out the exact rules for ordering of positional, keyword,
# star args, double star args and in which versions the various
# types of arguments are supported so we don't need to check that the
# expression compiles like this.
compile(function_call, '<string>', 'single')
except:
assume(False)
return function_call
def test_function_no_args():
validate_uncompyle("fn()")
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
def isolated_function_calls(which):
"""
Returns a strategy for generating function calls, but isolated to
particular types of arguments, for example only positional arguments.
This can help reason about debugging errors in specific types of function
calls.
:param which: One of 'keyword', 'positional', 'star', 'double_star'
:return: Strategy for generating an function call isolated to specific
argument types.
"""
kwargs = dict(
max_keyword_args=0,
max_positional_args=0,
max_star_args=0,
max_double_star_args=0,
)
kwargs['_'.join(('min', which, 'args'))] = 1
kwargs['_'.join(('max', which, 'args'))] = 5 if 'star' not in which else 1
return function_calls(**kwargs)
with settings(max_examples=25):
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('positional'))
@example("fn(0)")
def test_function_positional_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('keyword'))
@example("fn(a=0)")
def test_function_call_keyword_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('star'))
@example("fn(*items)")
def test_function_call_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('double_star'))
@example("fn(**{})")
def test_function_call_double_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.xfail()
def test_BUILD_CONST_KEY_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(w=0,m=0,**v)")
@pytest.mark.xfail()
def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(a=0,**g)")
@pytest.mark.xfail()
def test_CALL_FUNCTION_EX():
validate_uncompyle("fn(*g,**j)")
@pytest.mark.xfail()
def test_BUILD_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(*z,u=0)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(**a)")
@pytest.mark.xfail()
def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(b,b,b=0,*a)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(*c,v)")
@pytest.mark.xfail()
def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(i=0,y=0,*p)")
@pytest.mark.skip(reason='skipping property based test until all individual tests are passing')
@given(function_calls())
def test_function_call(function_call):
validate_uncompyle(function_call)

View File

@@ -8,8 +8,12 @@ from uncompyle6.semantics.consts import (
if PYTHON3:
from io import StringIO
def iteritems(d):
return d.items()
else:
from StringIO import StringIO
def iteritems(d):
return d.iteritems()
from uncompyle6.semantics.pysource import SourceWalker as SourceWalker
@@ -26,7 +30,7 @@ def test_template_engine():
# FIXME: and so on...
from uncompyle6.semantics.consts import (
TABLE_R, TABLE_DIRECT,
TABLE_DIRECT, TABLE_R,
)
from uncompyle6.semantics.fragments import (
@@ -40,7 +44,7 @@ def test_tables():
(TABLE_DIRECT, 'TABLE_DIRECT', False),
(TABLE_R, 'TABLE_R', False),
(TABLE_DIRECT_FRAGMENT, 'TABLE_DIRECT_FRAGMENT', True)):
for k, entry in t.iteritems():
for k, entry in iteritems(t):
if k in skip_for_now:
continue
fmt = entry[0]

View File

@@ -1,4 +1,7 @@
import pytest
from uncompyle6 import PYTHON_VERSION, code_deparse
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
if PYTHON_VERSION > 2.6:
def test_single_mode():

View File

@@ -9,4 +9,4 @@
12 JUMP_FORWARD 0 'to 15'
15_0 COME_FROM 12 '12'
15 LOAD_CONST 0 None
18 RETURN_VALUE
18 RETURN_VALUE

View File

@@ -12,4 +12,4 @@
18 STORE_NAME 2 'd'
21_0 COME_FROM 12 '12'
21 LOAD_CONST 2 None
24 RETURN_VALUE
24 RETURN_VALUE

View File

@@ -1,25 +1,28 @@
# future
from __future__ import print_function
# std
import os
import difflib
import subprocess
import tempfile
from StringIO import StringIO
import functools
# uncompyle6 / xdis
from uncompyle6 import PYTHON_VERSION, IS_PYPY, deparse_code
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY, deparse_code
# TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there
from xdis.bytecode import Bytecode
from xdis.main import get_opcode
opc = get_opcode(PYTHON_VERSION, IS_PYPY)
from StringIO import StringIO
Bytecode = functools.partial(Bytecode, opc=opc)
import six
if PYTHON3:
from io import StringIO
else:
from StringIO import StringIO
def _dis_to_text(co):
return Bytecode(co).dis()
try:
import functools
Bytecode = functools.partial(Bytecode, opc=opc)
def _dis_to_text(co):
return Bytecode(co).dis()
except:
pass
def print_diff(original, uncompyled):
"""
@@ -42,11 +45,8 @@ def print_diff(original, uncompyled):
print('\nTo display diff highlighting run:\n pip install BeautifulSoup4')
diff = difflib.HtmlDiff().make_table(*args)
f = tempfile.NamedTemporaryFile(delete=False)
try:
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(str(diff).encode('utf-8'))
finally:
f.close()
try:
print()
@@ -63,7 +63,8 @@ def print_diff(original, uncompyled):
print('\nFor side by side diff install elinks')
diff = difflib.Differ().compare(original_lines, uncompyled_lines)
print('\n'.join(diff))
os.unlink(f.name)
finally:
os.unlink(f.name)
def are_instructions_equal(i1, i2):
@@ -125,9 +126,8 @@ def validate_uncompyle(text, mode='exec'):
original_text = text
deparsed = deparse_code(PYTHON_VERSION, original_code,
compile_mode=mode,
out=StringIO(),
out=six.StringIO(),
is_pypy=IS_PYPY)
uncompyled_text = deparsed.text
uncompyled_code = compile(uncompyled_text, '<string>', 'exec')

View File

@@ -1,16 +1,16 @@
#!/usr/bin/env python
"""Setup script for the 'uncompyle6' distribution."""
import sys
"""Setup script for the 'uncompyle6' distribution."""
SYS_VERSION = sys.version_info[0:2]
if not ((2, 4) <= SYS_VERSION <= (2, 7)):
mess = "Python Release 2.4 .. 2.7 are supported in this code branch."
if ((3, 2) <= SYS_VERSION <= (3, 8)):
mess += ("\nFor your Python, version %s, use the master code/branch." %
if not ((2, 6) <= SYS_VERSION <= (3, 8)):
mess = "Python Release 2.6 .. 3.8 are supported in this code branch."
if ((2, 4) <= SYS_VERSION <= (2, 7)):
mess += ("\nFor your Python, version %s, use the python-2.4 code/branch." %
sys.version[0:3])
else:
mess += ("\nThis package is not supported before Python 2.4. Your Python version is %s."
elif SYS_VERSION < (2, 4):
mess += ("\nThis package is not supported for Python version %s."
% sys.version[0:3])
print(mess)
raise Exception(mess)

View File

@@ -1,55 +0,0 @@
import re
import unittest
from uncompyle6 import PYTHON_VERSION, IS_PYPY # , PYTHON_VERSION
from uncompyle6.parser import get_python_parser, python_parser
class TestGrammar(unittest.TestCase):
def test_grammar(self):
def check_tokens(tokens, opcode_set):
remain_tokens = set(tokens) - opcode_set
remain_tokens = set([re.sub('_\d+$','', t) for t in remain_tokens])
remain_tokens = set([re.sub('_CONT$','', t) for t in remain_tokens])
remain_tokens = set(remain_tokens) - opcode_set
self.assertEqual(remain_tokens, set([]),
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dump_grammar()))
p = get_python_parser(PYTHON_VERSION, is_pypy=IS_PYPY)
(lhs, rhs, tokens,
right_recursive, dup_rhs) = p.check_sets()
expect_lhs = set(['expr1024', 'pos_arg'])
unused_rhs = set(['list', 'call', 'mkfunc',
'mklambda',
'unpack',])
expect_right_recursive = frozenset([('designList',
('store', 'DUP_TOP', 'designList'))])
expect_lhs.add('kwarg')
self.assertEqual(expect_lhs, set(lhs))
self.assertEqual(unused_rhs, set(rhs))
self.assertEqual(expect_right_recursive, right_recursive)
expect_dup_rhs = frozenset([('COME_FROM',), ('CONTINUE',), ('JUMP_ABSOLUTE',),
('LOAD_CONST',),
('JUMP_BACK',), ('JUMP_FORWARD',)])
reduced_dup_rhs = {}
for k in dup_rhs:
if k not in expect_dup_rhs:
reduced_dup_rhs[k] = dup_rhs[k]
pass
pass
for k in reduced_dup_rhs:
print(k, reduced_dup_rhs[k])
# assert not reduced_dup_rhs, reduced_dup_rhs
def test_dup_rule(self):
import inspect
python_parser(PYTHON_VERSION, inspect.currentframe().f_code,
is_pypy=IS_PYPY,
parser_debug={
'dups': True, 'transition': False, 'reduce': False,
'rules': False, 'errorstack': None, 'context': True})
if __name__ == '__main__':
unittest.main()

View File

@@ -29,7 +29,7 @@ check:
$(MAKE) check-$(PYTHON_VERSION)
#: Run working tests from Python 2.6 or 2.7
check-2.4 check-2.5 check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-native-short
check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-native-short
#: Run working tests from Python 3.0
check-3.0: check-bytecode
@@ -148,7 +148,6 @@ check-bytecode-2.3:
#: Check deparsing Python 2.4
check-bytecode-2.4:
$(PYTHON) test_pythonlib.py --bytecode-2.4-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-2.4
#: Check deparsing Python 2.5
@@ -269,6 +268,7 @@ check-bytecode-3.6:
#: Check deparsing Python 3.7
check-bytecode-3.7:
$(PYTHON) test_pythonlib.py --bytecode-3.7-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.7 --weak-verify
#: Check deparsing Python 3.8
@@ -281,10 +281,6 @@ check-native-short:
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION)-run --verify-run $(COMPILE)
#: Run longer Python 2.6's lib files known to be okay
check-2.4-ok:
$(PYTHON) test_pythonlib.py --ok-2.4 --verify $(COMPILE)
#: Run longer Python 2.6's lib files known to be okay
check-2.6-ok:
$(PYTHON) test_pythonlib.py --ok-2.6 --weak-verify $(COMPILE)

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.

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.

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.

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.

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

@@ -1,8 +1,9 @@
#!/usr/bin/env python
# Mode: -*- python -*-
#
# Copyright (c) 2015, 2017 by Rocky Bernstein <rb@dustyfeet.com>
# Copyright (c) 2015 by Rocky Bernstein <rb@dustyfeet.com>
#
from __future__ import print_function
import dis, os.path

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python
from uncompyle6 import uncompyle
from __future__ import print_function
from uncompyle6.main import decompile
from xdis.magics import sysinfo2float
import sys, inspect

View File

@@ -22,7 +22,7 @@ assert i[0]('a') == True
assert i[0]('A') == False
# Issue #170. Bug is needing an "conditional_not_lambda" grammar rule
# in addition the the "conditional_lambda" rule
# in addition the the "if_expr_lambda" rule
j = lambda a: False if not a else True
assert j(True) == True
assert j(False) == False

View File

@@ -1,7 +0,0 @@
# From 2.4 test_array.py
# In Python 2.4 and earlier "yield" is not valid and instead
# we must use "yield None". Bug was not adding "None"
def yield_bug():
yield None
return

View File

@@ -1,19 +0,0 @@
# From 2.4 test_sax.py
# Bug was distinguishing try from try/else
def verify_empty_attrs():
gvqk = 3
try:
gvk = 1/0
except ZeroDivisionError:
gvk = 1
try:
gvqk = 0
except KeyError:
gvqk = 1
# If try/else was used above the return will be 4
return gvk + gvqk
assert 1 == verify_empty_attrs()

View File

@@ -2,3 +2,10 @@
# This is RUNNABLE!
assert [False, True, True, True, True] == [False if not a else True for a in range(5)]
assert [True, False, False, False, False] == [False if a else True for a in range(5)]
# From bug #225
m = ['hi', 'he', 'ih', 'who', 'ho']
ms = {}
for f in (f for f in m if f.startswith('h')):
ms[f] = 5
assert ms == {'hi': 5, 'he': 5, 'ho': 5}

View File

@@ -8,7 +8,7 @@ list(x for x in range(10) if x % 2 if x % 3)
# expresion which evaluates True unconditionally,
# but leave dead code or junk around that we have to match on.
# Tests "conditional_true" rule
# Tests "if_expr_true" rule
5 if 1 else 2
0 or max(5, 3) if 0 else 3

View File

@@ -0,0 +1,9 @@
# From 3.7.3 dataclasses.py
# Bug was handling precedence. Need parenthesis before IfExp.
#
# RUNNABLE!
def _hash_add(fields):
flds = [f for f in fields if (4 if f is None else f)]
return flds
assert _hash_add([None, True, False, 3]) == [None, True, 3]

View File

@@ -1,6 +1,6 @@
# Bug found in 2.7 test_itertools.py
# Bug was erroneously using reduction to unconditional_true
# A proper fix would be to use unconditional_true only when we
# Bug was erroneously using reduction to if_expr_true
# A proper fix would be to use if_expr_true only when we
# can determine there is or was dead code.
from itertools import izip_longest
for args in [['abc', range(6)]]:

View File

@@ -4,8 +4,8 @@
var1 = 'x'
var2 = 'y'
abc = 'def'
assert (f'interpolate {var1} strings {var2!r} {var2!s} py36' ==
"interpolate x strings 'y' y py36")
assert (f"interpolate {var1} strings {var2!r} {var2!s} 'py36" ==
"interpolate x strings 'y' y 'py36")
assert 'def0' == f'{abc}0'
assert 'defdef' == f'{abc}{abc!s}'
@@ -38,4 +38,31 @@ filename = '.'
source = 'foo'
source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n"
+ source + "\ndel __file__")
print(source)
# Note how { and } are *not* escaped here
f = 'one'
name = 'two'
assert(f"{f}{'{{name}}'} {f}{'{name}'}") == 'one{{name}} one{name}'
# From 3.7.3 dataclasses.py
log_rounds = 5
assert "05$" == f'{log_rounds:02d}$'
def testit(a, b, l):
# print(l)
return l
# The call below shows the need for BUILD_STRING to count expr arguments.
# Also note that we use {{ }} to escape braces in contrast to the example
# above.
def _repr_fn(fields):
return testit('__repr__',
('self',),
['return xx + f"(' +
', '.join([f"{f}={{self.{f}!r}}"
for f in fields]) +
')"'])
fields = ['a', 'b', 'c']
assert _repr_fn(fields) == ['return xx + f"(a={self.a!r}, b={self.b!r}, c={self.c!r})"']

View File

@@ -0,0 +1,47 @@
# From #227
# Bug was not handling call_ex_kw correctly
# This appears in
# showparams(c, test="A", **extra_args)
# below
def showparams(c, test, **extra_args):
return {'c': c, **extra_args, 'test': test}
def f(c, **extra_args):
return showparams(c, test="A", **extra_args)
def f1(c, d, **extra_args):
return showparams(c, test="B", **extra_args)
def f2(**extra_args):
return showparams(1, test="C", **extra_args)
def f3(c, *args, **extra_args):
return showparams(c, *args, **extra_args)
assert f(1, a=2, b=3) == {'c': 1, 'a': 2, 'b': 3, 'test': 'A'}
a = {'param1': 2}
assert f1('2', '{\'test\': "4"}', test2='a', **a) \
== {'c': '2', 'test2': 'a', 'param1': 2, 'test': 'B'}
assert f1(2, '"3"', test2='a', **a) \
== {'c': 2, 'test2': 'a', 'param1': 2, 'test': 'B'}
assert f1(False, '"3"', test2='a', **a) \
== {'c': False, 'test2': 'a', 'param1': 2, 'test': 'B'}
assert f(2, test2='A', **a) \
== {'c': 2, 'test2': 'A', 'param1': 2, 'test': 'A'}
assert f(str(2) + str(1), test2='a', **a) \
== {'c': '21', 'test2': 'a', 'param1': 2, 'test': 'A'}
assert f1((a.get('a'), a.get('b')), a, test3='A', **a) \
== {'c': (None, None), 'test3': 'A', 'param1': 2, 'test': 'B'}
b = {'b1': 1, 'b2': 2}
assert f2(**a, **b) == \
{'c': 1, 'param1': 2, 'b1': 1, 'b2': 2, 'test': 'C'}
c = (2,)
d = (2, 3)
assert f(2, **a) == {'c': 2, 'param1': 2, 'test': 'A'}
assert f3(2, *c, **a) == {'c': 2, 'param1': 2, 'test': 2}
assert f3(*d, **a) == {'c': 2, 'param1': 2, 'test': 3}

View File

@@ -0,0 +1,16 @@
# From 3.7.3 base64.py
# Bug was handling "and not" in an
# if/else in the presence of better Python bytecode generatation
# RUNNABLE!
def foo(foldnuls, word):
x = 5 if foldnuls and not word else 6
return x
for expect, foldnuls, word in (
(6, True, True),
(5, True, False),
(6, False, True),
(6, False, False)
):
assert foo(foldnuls, word) == expect

View File

@@ -11,9 +11,16 @@ def chained_compare_b(a, obj):
if -0x80000000 <= obj <= 0x7fffffff:
return 5
def chained_compare_c(a, d):
for i in len(d):
if a == d[i] != 2:
return 5
chained_compare_a(3)
try:
chained_compare_a(8)
except ValueError:
pass
chained_compare_b(True, 0x0)
chained_compare_c(3, [3])

View File

@@ -7,3 +7,4 @@ import http.client as httpclient
if len(__file__) == 0:
# a.b.c should force consecutive LOAD_ATTRs
import a.b.c as d
import stuff0.stuff1.stuff2.stuff3 as stuff3

View File

@@ -43,10 +43,10 @@ case $PYVERSION in
2.5)
SKIP_TESTS=(
[test_contextlib.py]=1 # Syntax error - look at
[test_dis.py]=1 # We change line numbers - duh!
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_grp.py]=1 # Long test - might work Control flow?
[test_pdb.py]=1 # Line-number specific
[test_dis.py]=1 # We change line numbers - duh!
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_grp.py]=1 # Long test - might work Control flow?
[test_pdb.py]=1 # Line-number specific
[test_pwd.py]=1 # Long test - might work? Control flow?
[test_queue.py]=1 # Control flow?
[test_re.py]=1 # Probably Control flow?

View File

@@ -20,7 +20,8 @@ Step 2: Run the test:
test_pyenvlib --mylib --verify # decompile verify 'mylib'
"""
from uncompyle6 import main, PYTHON3
from __future__ import print_function
import os, time, re, shutil, sys
from fnmatch import fnmatch

View File

@@ -27,6 +27,8 @@ Step 2: Run the test:
test_pythonlib.py --mylib --verify # decompile verify 'mylib'
"""
from __future__ import print_function
import getopt, os, py_compile, sys, shutil, tempfile, time
from uncompyle6 import PYTHON_VERSION
@@ -128,10 +130,8 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
if opts['do_compile']:
compiled_version = opts['compiled_version']
if compiled_version and PYTHON_VERSION != compiled_version:
sys.stderr.write("Not compiling: "
"desired Python version is %s "
"but we are running %s" %
(compiled_version, PYTHON_VERSION))
print("Not compiling: desired Python version is %s but we are running %s" %
(compiled_version, PYTHON_VERSION), file=sys.stderr)
else:
for root, dirs, basenames in os.walk(src_dir):
file_matches(files, root, basenames, PY)
@@ -149,8 +149,8 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
file_matches(files, dirname, basenames, obj_patterns)
if not files:
sys.stderr.write("Didn't come up with any files to test! "
"Try with --compile?")
print("Didn't come up with any files to test! Try with --compile?",
file=sys.stderr)
exit(1)
os.chdir(cwd)
@@ -164,9 +164,9 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
except ValueError:
pass
print time.ctime()
print 'Source directory: ', src_dir
print 'Output directory: ', target_dir
print(time.ctime())
print('Source directory: ', src_dir)
print('Output directory: ', target_dir)
try:
_, _, failed_files, failed_verify = \
main(src_dir, target_dir, files, [],
@@ -242,13 +242,14 @@ if __name__ == '__main__':
if os.path.isdir(src_dir):
checked_dirs.append([src_dir, pattern, target_dir])
else:
sys.stderr.write("Can't find directory %s. Skipping" % src_dir)
print("Can't find directory %s. Skipping" % src_dir,
file=sys.stderr)
continue
last_compile_version = compiled_version
pass
if not checked_dirs:
sys.stderr.write("No directories found to check\n")
print("No directories found to check", file=sys.stderr)
sys.exit(1)
test_opts['compiled_version'] = last_compile_version

View File

@@ -1 +0,0 @@
/.python-version

View File

@@ -3,6 +3,7 @@
#
# Copyright (c) 2015-2016, 2018 by Rocky Bernstein <rb@dustyfeet.com>
#
from __future__ import print_function
import sys, os, getopt
from uncompyle6.disas import disassemble_file
@@ -12,8 +13,8 @@ program, ext = os.path.splitext(os.path.basename(__file__))
__doc__ = """
Usage:
%s [OPTIONS]... FILE
%s [--help | -h | -V | --version]
{0} [OPTIONS]... FILE
{0} [--help | -h | -V | --version]
Disassemble FILE with the instruction mangling that is done to
assist uncompyle6 in parsing the instruction stream. For example
@@ -22,9 +23,9 @@ BUILD_LIST have arguement counts appended to the instruction name, and
COME_FROM instructions are inserted into the instruction stream.
Examples:
%s foo.pyc
%s foo.py # same thing as above but find the file
%s foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
{0} foo.pyc
{0} foo.py # same thing as above but find the file
{0} foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
See also `pydisasm' from the `xdis' package.
@@ -32,7 +33,7 @@ Options:
-V | --version show version and stop
-h | --help show this message
""" % ((program,) * 5)
""".format(program)
PATTERNS = ('*.pyc', '*.pyo')
@@ -41,15 +42,15 @@ def main():
Type -h for for full help.""" % program
if len(sys.argv) == 1:
sys.stderr.write("No file(s) given\n")
sys.stderr.write(Usage_short)
print("No file(s) given", file=sys.stderr)
print(Usage_short, file=sys.stderr)
sys.exit(1)
try:
opts, files = getopt.getopt(sys.argv[1:], 'hVU',
['help', 'version', 'uncompyle6'])
except getopt.GetoptError(e):
sys.stderr.write('%s: %s' % (os.path.basename(sys.argv[0]), e))
except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
sys.exit(-1)
for opt, val in opts:
@@ -61,14 +62,15 @@ Type -h for for full help.""" % program
sys.exit(0)
else:
print(opt)
sys.stderr.write(Usage_short)
print(Usage_short, file=sys.stderr)
sys.exit(1)
for file in files:
if os.path.exists(files[0]):
disassemble_file(file, sys.stdout)
else:
sys.stderr.write("Can't read %s - skipping\n" % files[0])
print("Can't read %s - skipping" % files[0],
file=sys.stderr)
pass
pass
return

View File

@@ -4,6 +4,7 @@
# Copyright (c) 2015-2017, 2019 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
from __future__ import print_function
import sys, os, getopt, time
program = 'uncompyle6'
@@ -66,9 +67,13 @@ def usage():
def main_bin():
if not (sys.version_info[0:2] in ((2, 4), (2, 5), (2, 6), (2, 7))):
sys.stderr.write('Error: this branch of %s requires Python 2.4, 2.5, 2.6 or 2.7'
% program)
if not (sys.version_info[0:2] in ((2, 6), (2, 7), (3, 0),
(3, 1), (3, 2), (3, 3),
(3, 4), (3, 5), (3, 6),
(3, 7), (3, 8)
)):
print('Error: %s requires Python 2.6-3.8' % program,
file=sys.stderr)
sys.exit(-1)
do_verify = recurse_dirs = False
@@ -86,8 +91,8 @@ def main_bin():
'fragments verify verify-run version '
'weak-verify '
'showgrammar'.split(' '))
except getopt.GetoptError(e):
sys.stderr.write('%s: %s\n' % (os.path.basename(sys.argv[0]), e))
except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
sys.exit(-1)
options = {}
@@ -130,7 +135,7 @@ def main_bin():
elif opt in ('--recurse', '-r'):
recurse_dirs = True
else:
sys.stderr.write(opt)
print(opt, file=sys.stderr)
usage()
# expand directory if specified
@@ -154,8 +159,8 @@ def main_bin():
sb_len = len( os.path.join(src_base, '') )
pyc_paths = [f[sb_len:] for f in pyc_paths]
if not pyc_paths:
sys.stderr.write("No files given\n")
if not pyc_paths and not source_paths:
print("No input files given to decompile", file=sys.stderr)
usage()
if outfile == '-':

View File

@@ -29,6 +29,8 @@ Second, we need structured instruction information for the
want to run on earlier Python versions.
"""
from __future__ import print_function
import sys
from collections import deque
@@ -47,9 +49,10 @@ def disco(version, co, out=None, is_pypy=False):
# store final output stream for case of error
real_out = out or sys.stdout
real_out.write('# Python %s\n' % version)
print('# Python %s' % version, file=real_out)
if co.co_filename:
real_out.write('# Embedded file name: %s\n' % co.co_filename)
print('# Embedded file name: %s' % co.co_filename,
file=real_out)
scanner = get_scanner(version, is_pypy=is_pypy)
@@ -61,15 +64,16 @@ def disco_loop(disasm, queue, real_out):
while len(queue) > 0:
co = queue.popleft()
if co.co_name != '<module>':
real_out.write('\n# %s line %d of %s\n' %
(co.co_name, co.co_firstlineno, co.co_filename))
print('\n# %s line %d of %s' %
(co.co_name, co.co_firstlineno, co.co_filename),
file=real_out)
tokens, customize = disasm(co)
for t in tokens:
if iscode(t.pattr):
queue.append(t.pattr)
elif iscode(t.attr):
queue.append(t.attr)
real_out.write(t)
print(t, file=real_out)
pass
pass

Some files were not shown because too many files have changed in this diff Show More