Compare commits

..

34 Commits

Author SHA1 Message Date
rocky
9415ad34ff One last grammar typo 2018-06-13 13:37:21 -04:00
rocky
476db9ad1d Get ready for release 3.2.3 2018-06-13 13:11:58 -04:00
rocky
9ba0b1bad2 Fix one more 3.0 parsing bug 2018-06-13 13:08:25 -04:00
rocky
b7942bc5f2 Add Python 1.3 decompilation ..
Reduced checking via "make check-short"
2018-06-13 12:26:21 -04:00
rocky
3ac4f1ee61 Had botched parameter order in 3.x. Sigh 2018-06-13 11:29:57 -04:00
rocky
55d2df04f7 Note that we handle Python 1.4 now 2018-06-12 15:28:40 -04:00
rocky
6d529d3cee Fix wording 2018-06-12 15:26:40 -04:00
rocky
201e5b18b1 Administrivia: Remove six dependency..
add version of hypothesis known to work
2018-06-12 14:44:08 -04:00
rocky
ac2bbfc65a Disable hypothesis on 2.6.9 2018-06-12 14:34:54 -04:00
rocky
3b8e6635e2 Get ready for release 3.2.2 2018-06-12 13:37:24 -04:00
rocky
7e172b63d1 Update documentation 2018-06-12 13:13:48 -04:00
rocky
c01eb554ed Fix bug introduced in last commit 2018-06-12 12:35:13 -04:00
rocky
d32e67891b More 3.0 bug fixing and tollerance and...
add some 1.4 bytecode tests
2018-06-12 12:19:43 -04:00
rocky
78b8d1cd06 Python 3.0 fixes + administrivia 2018-06-12 08:29:13 -04:00
rocky
600e56b1d7 Better "continue" detection on Python 3.0 2018-06-12 04:47:29 -04:00
rocky
170504c518 Remove unused 3.0 grammar rules 2018-06-11 11:54:08 -04:00
rocky
e3d918df3d Allow Python 3.0 and fix default param bug in 3.0 2018-06-11 11:33:50 -04:00
rocky
e7b62a722f Fix more Python 3.0 parse bugs 2018-06-10 16:49:04 -04:00
rocky
92d63ac598 More 3.0 grammar fixes...
3.0 is such as snowflake
2018-06-10 05:26:00 -04:00
rocky
79bed3419f last change left 3.2 finding comp_iter broken 2018-06-10 04:56:57 -04:00
rocky
0353b74a7a 3.0 list comprehensions 2018-06-09 23:14:04 -04:00
rocky
67910e7d8e Python 3.0 set comprehensions 2018-06-09 22:51:07 -04:00
rocky
61fa4fe391 Some Python 3.0 fixes...
Needs more in this direction though.
2018-06-09 10:05:23 -04:00
rocky
a8cdcc4d85 wrong test disabled 2018-06-04 15:35:17 -04:00
rocky
f0176add7a More bugs - note and disable tests for them 2018-06-04 15:29:04 -04:00
rocky
45c8d62e68 Get ready for release 3.2.1 2018-06-04 10:55:10 -04:00
rocky
096563cf91 Fix Python 1.5- bug in handling unpack list 2018-06-04 10:49:20 -04:00
rocky
7fd21aa227 Fix more Python 1.4 decompilation bugs 2018-06-04 09:37:35 -04:00
rocky
82bc294995 Improve Python 1.4 bytecode coverage 2018-06-04 08:54:09 -04:00
rocky
9d3e4a6660 Some Python 1.4 fixes 2018-06-04 02:09:48 -04:00
rocky
7dfade1195 Remove schmutz 2018-06-03 03:53:53 -04:00
rocky
1df5aa0ef9 Better Python 1.4 support 2018-06-03 03:21:15 -04:00
rocky
c06ba45991 See if we can get Appveyor working again... 2018-05-21 12:06:11 -04:00
rocky
7fab91eb4e Past fix of conditional_not bleed into 2.5...
and it shouldn't have
2018-05-19 13:00:44 -04:00
145 changed files with 1034 additions and 513 deletions

View File

@@ -3,7 +3,13 @@ language: python
sudo: false
python:
- '2.7' # this is a cheat here because travis doesn't do 2.4-2.6
- '3.5'
- '2.7'
- '2.6'
- '3.3'
- '3.4'
- '3.2'
- '3.6'
install:
- pip install -e .

View File

@@ -40,10 +40,6 @@ 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 $@
#:PyPy 2.6.1 PyPy 5.0.1, or PyPy 5.8.0-beta0
# Skip for now
2.6 5.0 5.3 5.6 5.8:

13
NEWS
View File

@@ -1,3 +1,16 @@
uncompyle6 3.2.3 2018-06-04 Michael Cohen flips and Fleetwood Redux
- Python 1.3 support 3.0 bug and
- fix botched parameter ordering of 3.x in last release
uncompyle6 3.2.2 2018-06-04 When I'm 64
- Python 3.0 support and bug fixes
uncompyle6 3.2.1 2018-06-04 MF
- Python 1.4 and 1.5 bug fixes
uncompyle6 3.2.0 2018-05-19 Rocket Scientist
- Add rudimentary 1.4 support (still a bit buggy)

View File

@@ -1,4 +1,4 @@
|buildstatus|
|buildstatus| |Latest Version| |Supported Python Versions|
uncompyle6
==========
@@ -11,8 +11,9 @@ Introduction
------------
*uncompyle6* translates Python bytecode back into equivalent Python
source code. It accepts bytecodes from Python version 1.5, and 2.1 to
3.7 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
source code. It accepts bytecodes from Python version 1.3 to version
3.7, spanning over 22 years of Python releases. We include Dropbox's
Python 2.5 bytecode and some PyPy bytecode.
Why this?
---------
@@ -29,11 +30,11 @@ CPython bytecode decompilers is the ability to deparse just
*fragments* of source code and give source-code information around a
given bytecode offset.
I use the tree fragments to deparse fragments of code inside my
trepan_ debuggers_. For that, bytecode offsets are recorded and
associated with fragments of the source code. This purpose, although
compatible with the original intention, is yet a little bit different.
See this_ for more information.
I use the tree fragments to deparse fragments of code *at run time*
inside my trepan_ debuggers_. For that, bytecode offsets are recorded
and associated with fragments of the source code. This purpose,
although compatible with the original intention, is yet a little bit
different. See this_ for more information.
Python fragment deparsing given an instruction offset is useful in
showing stack traces and can be encorporated into any program that
@@ -58,7 +59,7 @@ provides decompilation for subset of Python versions, we generally do
demonstrably better for those as well.
How can we tell? By taking Python bytecode that comes distributed with
that version of Python and decompiling these. Among htose that
that version of Python and decompiling these. Among those that
successfully decompile, we can then make sure the resulting programs
are syntactically correct by running the Python interpreter for that
bytecode version. Finally, in cases where the program has a test for
@@ -75,7 +76,7 @@ Requirements
The code here can be run on Python versions 2.6 or later, PyPy 3-2.4,
or PyPy-5.0.1. Python versions 2.4-2.7 are supported in the
python-2.4 branch. The bytecode files it can read have been tested on
Python bytecodes from versions 1.5, 2.1-2.7, and 3.0-3.6 and the
Python bytecodes from versions 1.4, 2.1-2.7, and 3.0-3.6 and the
above-mentioned PyPy versions.
Installation
@@ -151,12 +152,12 @@ 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 is a kind of *weak 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 use the same Python version in checking as was used
in creating the bytecode.
There 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
use the same Python version in checking as was used in creating the
bytecode.
There are however an interesting class of these programs that is
readily available give stronger verification: those programs that
@@ -174,19 +175,21 @@ that era was minimal)
There is some work to do on the lower end Python versions which is
more difficult for us to handle since we don't have a Python
interpreter for versions 1.5, 1.6, and 2.0.
interpreter for versions 1.6, and 2.0.
In the Python 3 series, Python support is is strongest around 3.4 or
3.3 and drops off as you move further away from those versions. Python
3.6 changes things drastically by using word codes rather than byte
codes. As a result, the jump offset field in a jump instruction
argument has been reduced. This makes the `EXTENDED_ARG` instructions
are now more prevalent in jump instruction; previously they had been
rare. Perhaps to compensate for the additional `EXTENDED_ARG`
instructions, additional jump optimization has been added. So in sum
handling control flow by ad hoc means as is currently done is worse.
3.0 is weird in that it in some ways resembles 2.6 more than it does
3.1 or 2.7. Python 3.6 changes things drastically by using word codes
rather than byte codes. As a result, the jump offset field in a jump
instruction argument has been reduced. This makes the `EXTENDED_ARG`
instructions are now more prevalent in jump instruction; previously
they had been rare. Perhaps to compensate for the additional
`EXTENDED_ARG` instructions, additional jump optimization has been
added. So in sum handling control flow by ad hoc means as is currently
done is worse.
Also, between Python 3.5, 3.6 and 3.7 there have been major changes to the
Between Python 3.5, 3.6 and 3.7 there have been major changes to the
`MAKE_FUNCTION` and `CALL_FUNCTION` instructions.
Currently not all Python magic numbers are supported. Specifically in
@@ -212,11 +215,12 @@ There is lots to do, so please dig in and help.
See Also
--------
* https://github.com/zrax/pycdc : supports all versions of Python and is written in C++. Support for Python 3 is a bit lacking though.
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique than what is used here.
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Includes some fixes like supporting function annotations
* The HISTORY_ file.
* https://github.com/zrax/pycdc : purports to support all versions of Python. It is written in C++ and is most accurate for Python versions around 2.7 and 3.3 when the code was more actively developed. Accuracy for more recent versions of Python 3 and early versions of Python are especially lacking. See its `issue tracker <https://github.com/zrax/pycdc/issues>`_ for details. Currently lightly maintained.
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique than what is used here. Currently unmaintained.
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Includes some fixes like supporting function annotations. Currently unmaintained.
* https://github.com/wibiti/uncompyle2 : supports Python 2.7 only, but does that fairly well. There are situtations where `uncompyle6` results are incorrect while `uncompyle2` results are not, but more often uncompyle6 is correct when uncompyle2 is not. Because `uncompyle6` adheres to accuracy over idiomatic Python, `uncompyle2` can produce more natural-looking code when it is correct. Currently `uncompyle2` is lightly maintained. See its issue `tracker <https://github.com/wibiti/uncompyle2/issues>`_ for more details
* `How to report a bug <https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md>`_
* The HISTORY_ file.
* https://github.com/rocky/python-xdis : Cross Python version disassembler
* https://github.com/rocky/python-xasm : Cross Python version assembler
* https://github.com/rocky/python-uncompyle6/wiki : Wiki Documents which describe the code and aspects of it in more detail
@@ -234,3 +238,6 @@ See Also
.. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84
.. _Deobfuscator: https://github.com/extremecoders-re/PjOrion-Deobfuscator
.. _Py2EXE: https://en.wikipedia.org/wiki/Py2exe
.. |Supported Python Versions| image:: https://img.shields.io/pypi/pyversions/uncompyle6.svg
.. |Latest Version| image:: https://badge.fury.io/py/uncompyle6.svg
:target: https://badge.fury.io/py/uncompyle6

View File

@@ -35,6 +35,7 @@ classifiers = ['Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
@@ -56,7 +57,8 @@ entry_points = {
]}
ftp_url = None
install_requires = ['spark-parser >= 1.8.5, < 1.9.0',
'xdis >= 3.8.2, < 3.9.0']
'xdis >= 3.8.4, < 3.9.0']
license = 'GPL3'
mailing_list = 'python-debugger@googlegroups.com'
modname = 'uncompyle6'

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

@@ -16,6 +16,7 @@ if ! source ./setup-master.sh ; then
fi
cd ..
for version in $PYVERSIONS; do
echo --- $version ---
if ! pyenv local $version ; then
exit $?
fi
@@ -23,4 +24,5 @@ for version in $PYVERSIONS; do
if ! make check; then
exit $?
fi
echo === $version ===
done

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

@@ -15,6 +15,7 @@ fi
cd ..
for version in $PYVERSIONS; do
echo --- $version ---
if ! pyenv local $version ; then
exit $?
fi
@@ -22,4 +23,5 @@ for version in $PYVERSIONS; do
if ! make check ; then
exit $?
fi
echo === $version ===
done

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}*

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

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

@@ -47,7 +47,7 @@ install:
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "pip install --disable-pip-version-check --user --upgrade pip"
- "%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,

View File

@@ -12,4 +12,4 @@ dependencies:
test:
override:
- python ./setup.py develop && make check-2.7
- cd ./test/stdlib && pyenv local 2.7.10 && bash ./runtests.sh 'test_[f-i]*.py'
- cd ./test/stdlib && pyenv local 2.7.10 && bash ./runtests.sh 'test_[p-z]*.py'

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)

154
pytest/test_fstring.py Normal file
View File

@@ -0,0 +1,154 @@
# 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 at least 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:
assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')'
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least 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 at least 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,6 +1,9 @@
import pytest
from uncompyle6 import PYTHON_VERSION, deparse_code
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
if PYTHON_VERSION >= 2.6:
if PYTHON_VERSION > 2.6:
def test_single_mode():
single_expressions = (
'i = 1',

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,26 @@
# 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)
Bytecode = functools.partial(Bytecode, opc=opc)
try:
import functools
Bytecode = functools.partial(Bytecode, opc=opc)
def _dis_to_text(co):
return Bytecode(co).dis()
except:
pass
if PYTHON3:
from io import StringIO
else:
from StringIO import StringIO
def _dis_to_text(co):
return Bytecode(co).dis()
def print_diff(original, uncompyled):
@@ -43,11 +44,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()
@@ -64,7 +62,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):
@@ -126,9 +125,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,2 +1,4 @@
# Pick up stuff from setup.py
hypothesis==2.0.0
pytest
-e .

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, 7)):
mess += ("\nFor your Python, version %s, use the master code/branch." %
if not ((2, 6) <= SYS_VERSION <= (3, 7)):
mess = "Python Release 2.6 .. 3.7 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

@@ -22,14 +22,14 @@ COVER_DIR=../tmp/grammar-cover
# Run short tests
check-short:
@$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
$(MAKE) check-bytecode
$(MAKE) check-bytecode-short
# Run all tests
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
@@ -91,16 +91,31 @@ check-bytecode-3:
--bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \
--bytecode-3.4 --bytecode-3.5 --bytecode-3.6 --bytecode-pypy3.2
#: Check deparsing bytecode that works running Python 2 and Python 3
#: Check deparsing on selected bytecode 3.x
check-bytecode-3-short:
$(PYTHON) test_pythonlib.py \
--bytecode-3.4 --bytecode-3.5 --bytecode-3.6
#: Check deparsing bytecode on all Python 2 and Python 3 versions
check-bytecode: check-bytecode-3
$(PYTHON) test_pythonlib.py \
--bytecode-1.4 --bytecode-1.5 \
--bytecode-1.3 --bytecode-1.4 --bytecode-1.5 \
--bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \
--bytecode-2.1 --bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \
--bytecode-2.5 --bytecode-2.6 --bytecode-2.7 \
--bytecode-pypy2.7
#: Check deparsing bytecode on selected Python 2 and Python 3 versions
check-bytecode-short: check-bytecode-3-short
$(PYTHON) test_pythonlib.py \
--bytecode-2.6 --bytecode-2.7 --bytecode-pypy2.7
#: Check deparsing bytecode 1.3 only
check-bytecode-1.3:
$(PYTHON) test_pythonlib.py --bytecode-1.3
#: Check deparsing bytecode 1.4 only
check-bytecode-1.4:
$(PYTHON) test_pythonlib.py --bytecode-1.4
@@ -210,6 +225,11 @@ check-bytecode-2.7:
check-bytecode-3.0:
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify
#: Check deparsing Python 3.0
check-bytecode-3.0:
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify
# $(PYTHON) test_pythonlib.py --bytecode-3.0-run --verify-run
#: Check deparsing Python 3.1
check-bytecode-3.1:
$(PYTHON) test_pythonlib.py --bytecode-3.1 --weak-verify
@@ -249,10 +269,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)
@@ -274,7 +290,7 @@ check-3.4-ok:
2.6:
#: PyPy 5.0.x with Python 2.7 ...
pypy-2.7 5.0 5.3:
pypy-2.7 5.0 5.3 6.0:
$(PYTHON) test_pythonlib.py --bytecode-pypy2.7 --verify
#: PyPy 2.4.x with Python 3.2 ...

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.

BIN
test/bytecode_1.4/cmp.pyc Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
test/bytecode_1.4/glob.pyc Normal file

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,5 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from uncompyle6 import uncompyle
import sys, inspect

View File

@@ -0,0 +1,3 @@
# Python 1.4 tzparse.py, but also appears in 1.5
[tzname, delta] = __file__

View File

@@ -0,0 +1,6 @@
# Python 1.4 aifc.py
# Something weird about the final "print" and PRINT_NL_CONT followed by PRINT_NL
def _readmark(self, markers):
if self._markers: print 'marker',
else: print 'markers',
print 'instead of', markers

View File

@@ -0,0 +1,11 @@
# Python 1.4 cgi.py
# Bug was in "continue" detection.
# 1.4 doesn't have lnotab and our CONTINUE detection is off.
def parse_multipart(params, pdict):
while params:
if params.has_key('name'):
params = None
else:
continue
return None

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

@@ -0,0 +1,20 @@
# From 3.0.1 __dummy_thread.py
# bug was handling else:
def interrupt_main():
"""Set _interrupt flag to True to have start_new_thread raise
KeyboardInterrupt upon exiting."""
if _main:
raise KeyboardInterrupt
else:
global _interrupt
_interrupt = True
# From 3.0.1 ast.py bug was mangling prototype
# def parse(expr, filename='<unknown>', mode='exec'):
# From 3.0.1 bisect
def bisect_left(a, x, lo=0, hi=None):
while lo:
if a[mid] < x: lo = mid+1
else: hi = mid
return lo

22
test/stdlib/compile-file-1x.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""byte compiles a Python 1.x program"""
import sys
if len(sys.argv) != 2:
print("Usage: compile-file.py *python-file*")
sys.exit(1)
source = sys.argv[1]
# assert source.endswith('.py')
basename = source[:-3]
# We do this crazy way to support Python 1.4 which
# doesn't support version_info.
PY_VERSION = sys.version[:3]
bytecode = "%s-%s.pyc" % (basename, PY_VERSION)
import py_compile
print("# compiling %s to %s" % (source, bytecode))
py_compile.compile(source, bytecode)
# import os
# os.system("../bin/uncompyle6 %s" % bytecode)

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
"""byte compiles a Python program after version 2.2 or so. Also see compile_file_1x.py"""
import sys
if len(sys.argv) != 2:
print("Usage: compile-file.py *python-file*")

View File

@@ -42,10 +42,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?
@@ -105,6 +105,7 @@ case $PYVERSION in
[test_curses.py]=1 # Possibly fails on its own but not detected
[test_dis.py]=1 # We change line numbers - duh!
[test_doctest.py]=1 # Fails on its own
[test_format.py]=1 # control flow. uncompyle2 does not have problems here
[test_generators.py]=1 # control flow. uncompyle2 has problem here too
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_io.py]=1 # Test takes too long to run

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
@@ -76,7 +78,7 @@ for vers in (2.7, 3.4, 3.5, 3.6):
test_options[key] = (os.path.join(src_dir, pythonlib), PYOC, key, vers)
pass
for vers in (1.4, 1.5,
for vers in (1.3, 1.4, 1.5,
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, 'pypy3.2', 'pypy2.7'):
@@ -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

@@ -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

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