Compare commits

...

129 Commits

Author SHA1 Message Date
rocky
350a16cfa2 Administrivia 2018-10-27 12:54:25 -04:00
rocky
5a7024d2a3 Tweak travis 2018-10-27 11:30:49 -04:00
rocky
e16138527a Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-10-27 11:23:02 -04:00
rocky
2362045c84 Get ready for release 2018-10-27 11:21:05 -04:00
rocky
a29afd6832 Get ready for release 2018-10-27 11:20:07 -04:00
rocky
1bcf0d54f7 Get ready for release 3.2.4 2018-10-27 11:04:26 -04:00
rocky
4c6bdd58ab Fix indentation iftrue_stmt24
Fixes #187
2018-10-17 15:35:51 -04:00
rocky
7898fa157b extend Python 2.6- lastc grammar-rule
Fixes #192
2018-10-05 12:37:16 -04:00
rocky
bbdb238ddb Note that bytecode should be provided. 2018-10-01 23:59:06 -04:00
rocky
ec42ee540c Small typos 2018-09-20 17:40:23 -04:00
rocky
616e5c82f6 Reinstat expr32 and expr1024 rules...
to speed up handling long literal lists. See also issue #188

Update issue forms to simplfy via putting instructions as comments.
2018-09-19 20:14:42 -04:00
rocky
1cb31a85fd decompile bytecode_version defaults to Python intepreter version
Fixes #189
2018-09-19 15:20:23 -04:00
rocky
7d1ecf957c Not sure why this is here, but fix it so it works. 2018-09-19 08:41:02 -04:00
rocky
c96e796ff5 Handle Python 2.4 if true 2018-08-12 02:24:13 -04:00
rocky
f14e1dd62f Update bug report template 2018-08-02 12:32:50 -04:00
rocky
5f1667988f Guidleines for reporting bugs and openning feature requests 2018-08-02 12:28:58 -04:00
rocky
19ec52eb63 Change AST to SyntaxTree in many places 2018-07-15 12:37:50 -04:00
rocky
c3e722ad51 Python 3.7 is too new for TravisCI 2018-07-13 10:39:48 -04:00
rocky
31de342528 Try 3.7.0 on travis 2018-07-13 10:32:10 -04:00
rocky
70f25d5f8d Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-07-05 21:48:53 -04:00
rocky
1b71cfef07 3.6 omit END_FINALLY sometimes
Fixes #182
2018-07-05 21:47:36 -04:00
R. Bernstein
e7845ed2e4 Update HOW-TO-REPORT-A-BUG.md 2018-07-05 07:19:33 -04:00
rocky
f4a1e9e40f Skip botched 3.8.5 release 2018-07-03 16:32:23 -04:00
rocky
b9e8e619f6 Add a 3.0 test 2018-07-03 15:45:27 -04:00
rocky
5c88f804c1 Remove CircleCI 1.1 2018-06-26 22:36:51 -04:00
rocky
35834b06d4 Generalize stdlib test 2018-06-25 16:43:06 -04:00
rocky
27febca918 Another CircleCI 2.0 try 2018-06-25 13:18:14 -04:00
rocky
37b917ed7c Convert to CircleCI 2.0 2018-06-25 13:12:46 -04:00
rocky
82499b50bb Small typo 2018-06-24 18:24:53 -04:00
rocky
c6b3e20b47 Remove some of the 3.0 3.x instruction hackiness 2018-06-24 18:23:38 -04:00
rocky
93e889e82a Python 3.0 comprehensions are a snowflake 2018-06-24 16:46:56 -04:00
rocky
4cbd136635 Make Python 3.0 control flow more like 3.x 2018-06-24 12:56:43 -04:00
rocky
488a14488c Python 3.0 compare-chained2 grammar rule 2018-06-24 11:19:10 -04:00
rocky
44321fcedf Extend Python 3.0 "or" grammar rule 2018-06-24 11:07:37 -04:00
rocky
1e0a6d528e Expand 3.0 while grammar rule 2018-06-24 07:42:36 -04:00
rocky
40fa379fd9 Add 3.0 comp_if_not grammar rule 2018-06-24 07:28:58 -04:00
rocky
34ec41f274 Improve 3.0 list comprehensions 2018-06-24 06:56:30 -04:00
rocky
3daf3732c3 Fix Python 3.0 "and" parse rule 2018-06-23 23:04:17 -04:00
rocky
988efa3693 Had botched 2.6 grammar 2018-06-23 13:48:01 -04:00
rocky
bc16cc93d6 More Python 3.0 parse errors 2018-06-23 13:31:16 -04:00
rocky
8d1bd6d5b5 Fix one more Python 3.0 parse bug 2018-06-23 12:24:22 -04:00
rocky
c148e49670 Python 3.0 chained comparisions 2018-06-23 07:38:34 -04:00
rocky
fb31fe1f35 Another Python 3.0 (while) parse bug 2018-06-23 05:48:14 -04:00
rocky
fa5da2b1ef Fix 3.0 try except bug 2018-06-23 00:06:35 -04:00
rocky
f331deb864 2nd attempt to get check_grammar working on 3.x 2018-06-22 21:27:46 -04:00
rocky
1350f4c899 Some bugs...
Python 3 compiling Python 2 tolerance. Fixes issue #180.
pytest test_grammar.py and validate.py fixes for 3.6 testing
2018-06-22 21:06:54 -04:00
rocky
6fd8d2556b Fix another 3.0 bug 2018-06-22 14:32:52 -04:00
rocky
276fb77e71 Fix two Python 3.0 bugs...
* don't add _[0] list comprehension variables
* add POP_TOP in _ifstmts_jmp; c_stmst for now isn't optional
2018-06-22 09:58:28 -04:00
rocky
f547ec9291 Better is_pypy defaults 2018-06-19 12:26:22 -04:00
rocky
4f994b4cae Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-06-19 04:08:58 -04:00
rocky
98cd06d6e0 Remove a use of deparse_code. 2018-06-19 04:08:20 -04:00
rocky
a3b6806c33 Omit another test on gcc110 2018-06-18 11:19:51 -04:00
rocky
66ebb15d42 Previous two releases botched iterated list comprehensions 2018-06-14 10:32:54 -04:00
rocky
8955788990 Add first 3.0 run test 2018-06-13 18:47:36 -04:00
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
rocky
c2181e3235 Get ready for release 3.2.0 2018-05-19 12:29:26 -04:00
rocky
3695921364 CircleCI administrivia 2018-05-19 11:59:55 -04:00
rocky
d14193f219 xdis 3.8.2 has python 1.4 support 2018-05-19 11:53:32 -04:00
rocky
94251cd294 Tolerate bytecode < 1.5 2018-05-19 11:49:42 -04:00
rocky
a9515c7aab Add bytecode 1.4 small tests
Many bugs in 1.4 exist. For a future release
2018-05-19 11:31:23 -04:00
rocky
e5f3d803a8 Start Python 1.4 decompilation ...
Tidy up test code for issue 162 and comments for some disassembly massaging.
2018-05-19 07:14:00 -04:00
rocky
e5ae70bea8 batch workaround 2018-05-14 22:28:54 -04:00
rocky
189605ea2c Adjust showtree() calls 2018-05-13 14:23:52 -04:00
rocky
4c74bf1d9d --tree++ shows template rule when it is used 2018-05-13 14:21:46 -04:00
rocky
c087bd785e Tweak 2.6.9 for batch test machine 2018-05-11 09:57:08 -04:00
rocky
80b68af2d3 More 2.6 control flow logix futzing 2018-05-09 11:12:16 -04:00
rocky
24ccc16701 Need full filename in runtests.sh 2018-05-08 10:38:50 -04:00
rocky
69714fb65a Limit more tests in batch 2018-05-08 10:30:59 -04:00
rocky
b94f98f8f7 Note we can't handle try/else sometimes in 2.7 2018-05-08 10:17:38 -04:00
rocky
f05b092983 "and" handling before 2.6 is different 2018-05-08 09:29:41 -04:00
rocky
76a66c3460 2.6 if-else-not handling...
For now, we say that conditional-not can't be in an "and".
2018-05-08 09:17:20 -04:00
rocky
91224b2382 Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-05-08 08:50:23 -04:00
rocky
e76f1f107f Better testing of named %c format specifiers 2018-05-08 08:49:43 -04:00
rocky
2f8e063a99 remove 2.6.9 test_grammar.py for now 2018-05-03 21:20:22 -04:00
rocky
15533c5e38 Fixes #174 2018-05-03 14:56:18 -04:00
rocky
6511cc4dd4 Add Another 2.7.5 "while 1" rule 2018-05-01 04:00:35 -04:00
rocky
fdf97a1cc0 Add if_not rule for Python 2.7 2018-05-01 03:10:46 -04:00
rocky
24011bb0da Python 2.7.5 tolerance 2018-05-01 02:48:51 -04:00
rocky
4f61321c91 stdlib batch testing workarounds 2018-04-29 10:03:47 -04:00
rocky
269f4f2e1b 2.6, 2.7 Parse if else inside list comprehension
Fixes #171
2018-04-28 20:44:09 -04:00
rocky
aab951280b Set precedence better for list comprehensions. 2018-04-28 17:27:29 -04:00
rocky
f1e48fb60a while1 grammar rule cleanup
Closes #172
2018-04-27 10:57:27 -04:00
rocky
c0022ed5b7 Typo 2018-04-25 13:05:56 -04:00
rocky
41a50b5e46 Handle if not else in lambdas...
Fixes #170
2018-04-25 12:57:09 -04:00
rocky
0154c87d63 CALL_FUNCTION_EX specialization in 3.6 2018-04-23 18:47:30 -04:00
rocky
c9c70103aa Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-04-23 15:40:19 -04:00
rocky
a18dc340ce Start to narrow 3.7 FUNCTION_EX grammar 2018-04-23 15:40:04 -04:00
rocky
037648577f Start to narrow 3.7 FUNCTION_EX grammar 2018-04-23 15:16:03 -04:00
rocky
cd62e54c88 Correct (3.7) use fof BUILD_MAP_UNPACK_WITH_CALL 2018-04-21 00:41:14 -04:00
rocky
ef9ccc3a8c Fix 3.7 aysnc def testing 2018-04-20 11:15:52 -04:00
rocky
c397bf6bda Generalize 3.7 attribute with LOAD_METHOD 2018-04-19 19:28:53 -04:00
rocky
0aa41058a6 customize "async for" on 3.6. and 3.7 2018-04-19 15:03:09 -04:00
rocky
27f67e6fca Fix some 3.6/3.7 bugs 2018-04-19 10:00:40 -04:00
rocky
6fcf49b214 2.6 compatability 2018-04-18 12:18:32 -04:00
rocky
49661b222e Ooops - remove debug statement. 2018-04-18 12:11:56 -04:00
rocky
c481d97866 A more uniform way to track opcodes seen...
use a set rather than these boolean variables. Done in 3.x
only for now. May do more later..
2018-04-18 12:01:46 -04:00
168 changed files with 2071 additions and 797 deletions

89
.circleci/config.yml Normal file
View File

@@ -0,0 +1,89 @@
# This configuration was automatically generated from a CircleCI 1.0 config.
# It should include any build commands you had along with commands that CircleCI
# inferred from your project structure. We strongly recommend you read all the
# comments in this file to understand the structure of CircleCI 2.0, as the idiom
# for configuration has changed substantially in 2.0 to allow arbitrary jobs rather
# than the prescribed lifecycle of 1.0. In general, we recommend using this generated
# configuration as a reference rather than using it in production, though in most
# cases it should duplicate the execution of your original 1.0 config.
version: 2
jobs:
build:
working_directory: ~/rocky/python-uncompyle6
parallelism: 1
shell: /bin/bash --login
# CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did.
# If any of these refer to each other, rewrite them so that they don't or see https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables .
environment:
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
COMPILE: --compile
# In CircleCI 1.0 we used a pre-configured image with a large number of languages and other packages.
# In CircleCI 2.0 you can now specify your own image, or use one of our pre-configured images.
# The following configuration line tells CircleCI to use the specified docker image as the runtime environment for you job.
# We have selected a pre-built image that mirrors the build environment we use on
# the 1.0 platform, but we recommend you choose an image more tailored to the needs
# of each job. For more information on choosing an image (or alternatively using a
# VM instead of a container) see https://circleci.com/docs/2.0/executor-types/
# To see the list of pre-built images that CircleCI provides for most common languages see
# https://circleci.com/docs/2.0/circleci-images/
docker:
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
command: /sbin/init
steps:
# Machine Setup
# 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
# The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out.
- checkout
# Prepare for artifact and test results collection equivalent to how it was done on 1.0.
# In many cases you can simplify this from what is generated here.
# 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/'
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
# This is based on your 1.0 configuration file or project settings
- run:
working_directory: ~/rocky/python-uncompyle6
command: pyenv install 2.7.13 && pyenv local 2.7.13 && 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
- restore_cache:
keys:
# This branch if available
- v1-dep-{{ .Branch }}-
# Default branch if not
- v1-dep-master-
# 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: pip install --upgrade setuptools
- run: pip install -e .
- run: pip install pytest==3.2.5 hypothesis
# Save dependency cache
- save_cache:
key: v1-dep-{{ .Branch }}-{{ epoch }}
paths:
# This is a broad list of cache paths to include many possible development environments
# You can probably delete some of these entries
- vendor/bundle
- ~/virtualenvs
- ~/.m2
- ~/.ivy2
- ~/.bundle
- ~/.go_workspace
- ~/.gradle
- ~/.cache/bower
# 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.7
- run: cd ./test/stdlib && pyenv local 2.7.13 && 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
- store_test_results:
path: /tmp/circleci-test-results
# Save artifacts
- store_artifacts:
path: /tmp/circleci-artifacts
- store_artifacts:
path: /tmp/circleci-test-results

77
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,77 @@
---
name: Bug report
about: Tell us about uncompyle6 bugs
---
<!-- __Note:__ Have you read https://github.com/rocky/python-uncompyle6/blob/master/HOW-TO-REPORT-A-BUG.md ?
Please remove any of the optional sections if they are not applicable.
Prerequisites
* Make sure the bytecode you have can be disassembled with a
disassembler.
* Don't put bytecode and corresponding source code on any service that
requires registration to download.
* When you open a bug report there is no privacy. If the legitimacy of
the activity is deemed suspicous, I may flag it as suspicious,
making the issue even more easy to detect.
Bug reports that violate a prerequisite may be discarded.
Note that there are way more bug-fix requestors than there are bug
fixers. If you want you need more immediate, confidential or urgent
assistance
http://www.crazy-compilers.com/decompyle/ offers a byte-code
decompiler service for versions of Python up to 2.6.
-->
## Description
<!-- Add a clear and concise description of the bug. -->
## How to Reproduce
<!-- Please show both the input you gave and the
output you got in describing how to reproduce the bug:
or give a complete console log with input and output
```console
$ uncompyle6 <command-line-options>
...
$
```
Provide links to the Python bytecode. For example you can create a
gist with the information. If you have the correct source code, you
can add that too.
-->
## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->
## Environment
<!-- _This section sometimes is optional but helpful to us._
Please modify for your setup
- Uncompyle6 version: output from `uncompyle6 --version` or `pip show uncompyle6`
- Python version: `python -V`
- OS and Version: [e.g. Ubuntu bionic]
-->
## Additional Environment or Context
<!-- _This section is optional._
Add any other context about the problem here or special environment setup.
-->

View File

@@ -0,0 +1,22 @@
---
name: Feature Request
about: Tell us about a new feature that you would like to see in uncompyle6
---
## Description
<!-- Add a short description of the feature. This might
include same input and output. -->
## Background
<!-- Add any additional background for the
feature, for example: user scenarios, or the value of the feature. -->
## Tests
<!-- _This section is optional._
Add text with suggestions on how to test the feature,
if it is not obvious.
-->

View File

@@ -6,9 +6,7 @@ python:
- '3.5'
- '2.7'
- '2.6'
- '3.3'
- '3.4'
- '3.2'
- '3.6'
install:

View File

@@ -33,7 +33,7 @@ prescribed cases, the ill-defined amorphous cases as well will get
handled as well.
In sum, you may need to do some work to have the bug you have found
handled before the hundreds of other bugs, and things I could be
handled before the hundreds of other bugs, and other things I could be
doing.
No one is getting paid to work to work on this project, let alone the

View File

@@ -38,6 +38,7 @@ check-3.0 check-3.1 check-3.2 check-3.6:
$(MAKE) -C test $@
check-3.7: pytest
$(MAKE) -C test check
#:PyPy 2.6.1 PyPy 5.0.1, or PyPy 5.8.0-beta0
# Skip for now

26
NEWS
View File

@@ -1,3 +1,29 @@
uncompyle6 3.2.4 2018-06-04 7x9 release
- Bug fixes #180, #182, #187, #192
- Enhancements #189
- Internal improvements
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)
- add --tree+ option to show formatting rule, when it is constant
- Python 2.7.15candidate1 support (via xdis)
- bug fixes, especially for 3.7 (but 2.7 and 3.6 and others as well)
uncompyle6 3.1.3 2018-04-16
- Add some Python 3.7 rules, such as for handling LOAD_METHOD (not complete)

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',
@@ -55,8 +56,8 @@ entry_points = {
'pydisassemble=uncompyle6.bin.pydisassemble:main',
]}
ftp_url = None
install_requires = ['spark-parser >= 1.8.5, < 1.9.0',
'xdis >= 3.8.0, < 3.9.0', 'six']
install_requires = ['spark-parser >= 1.8.7, < 1.9.0',
'xdis >= 3.8.8, < 3.9.0']
license = 'GPL3'
mailing_list = 'python-debugger@googlegroups.com'

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

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

@@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS='3.5.5 3.6.5 2.6.9 3.3.7 2.7.14 3.2.6 3.1.5 3.4.8'
export PYVERSIONS='3.5.5 3.6.6 3.7.1 2.6.9 3.3.7 2.7.15 3.2.6 3.1.5 3.4.8'

View File

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

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

@@ -1,14 +0,0 @@
machine:
python:
version: 2.7.10
environment:
COMPILE: --compile
dependencies:
override:
- pip install -e .
- pip install pytest==3.2.5 hypothesis
test:
override:
- python ./setup.py develop && make check-2.7
- cd ./test/stdlib && pyenv local 2.7.10 && bash ./runtests.sh 'test_[p-z]*.py'

View File

@@ -1,150 +1,154 @@
# std
import os
# test
import pytest
import hypothesis
from hypothesis import strategies as st
# uncompyle6
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 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:
@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" | "%"
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
See https://docs.python.org/2/library/string.html
:param draw: Let hypothesis draw from other strategies.
: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 ''
: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'
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 ''
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)
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 ''
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 = ''
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_,))
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
@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.
: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)
: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
@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') + ')'
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')
@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)
@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

@@ -1,184 +1,185 @@
# std
import string
# 3rd party
from hypothesis import given, assume, example, settings, strategies as st
import pytest
# uncompyle
from validate import validate_uncompyle
from test_fstring import expressions
from uncompyle6 import PYTHON_VERSION
import pytest
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits)
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
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.
@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.
: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
)
: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)]
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)
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
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):
def test_function_no_args():
validate_uncompyle("fn()")
@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)
def isolated_function_calls(which):
"""
Returns a strategy for generating function calls, but isolated to
particular types of arguments, for example only positional arguments.
@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)
This can help reason about debugging errors in specific types of function
calls.
@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)
:param which: One of 'keyword', 'positional', 'star', 'double_star'
@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)
: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)
@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)")
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_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(a=0,**g)")
@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_CALL_FUNCTION_EX():
validate_uncompyle("fn(*g,**j)")
@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_BUILD_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(*z,u=0)")
@pytest.mark.xfail()
def test_CALL_FUNCTION_EX():
validate_uncompyle("fn(*g,**j)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(**a)")
@pytest.mark.xfail()
def test_BUILD_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(*z,u=0)")
@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_CALL_FUNCTION_EX():
validate_uncompyle("fn(**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_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_CONST_KEY_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(i=0,y=0,*p)")
@pytest.mark.xfail()
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(*c,v)")
@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)
@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

@@ -46,13 +46,11 @@ def test_grammar():
unused_rhs.add("mkfunc_annotate")
unused_rhs.add("dict_comp")
unused_rhs.add("classdefdeco1")
if PYTHON_VERSION != 3.6:
if PYTHON_VERSION in (3.5, 3.7):
expect_right_recursive.add((('l_stmts',
('lastl_stmt', 'come_froms', 'l_stmts'))))
pass
if PYTHON_VERSION >= 3.5:
expect_right_recursive.add((('l_stmts',
('lastl_stmt', 'come_froms', 'l_stmts'))))
pass
else:
elif 3.0 < PYTHON_VERSION < 3.3:
expect_right_recursive.add((('l_stmts',
('lastl_stmt', 'COME_FROM', 'l_stmts'))))
pass

View File

@@ -1,21 +1,22 @@
import pytest
from uncompyle6 import PYTHON_VERSION, deparse_code
from uncompyle6 import PYTHON_VERSION, code_deparse
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
def test_single_mode():
single_expressions = (
'i = 1',
'i and (j or k)',
'i += 1',
'i = j % 4',
'i = {}',
'i = []',
'for i in range(10):\n i\n',
'for i in range(10):\n for j in range(10):\n i + j\n',
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
)
if PYTHON_VERSION > 2.6:
def test_single_mode():
single_expressions = (
'i = 1',
'i and (j or k)',
'i += 1',
'i = j % 4',
'i = {}',
'i = []',
'for i in range(10):\n i\n',
'for i in range(10):\n for j in range(10):\n i + j\n',
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
)
for expr in single_expressions:
code = compile(expr + '\n', '<string>', 'single')
assert deparse_code(PYTHON_VERSION, code, compile_mode='single').text == expr + '\n'
for expr in single_expressions:
code = compile(expr + '\n', '<string>', 'single')
assert code_deparse(code, compile_mode='single').text == expr + '\n'

View File

@@ -6,16 +6,19 @@ import difflib
import subprocess
import tempfile
import functools
# compatability
import six
# 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)
import six
if PYTHON3:
from io import StringIO
else:
from StringIO import StringIO
def _dis_to_text(co):
return Bytecode(co).dis()

View File

@@ -1,3 +1,2 @@
pytest>=3.0.0,<=3.0.1
flake8
hypothesis<=3.0.0

View File

@@ -1,2 +1,4 @@
# Pick up stuff from setup.py
hypothesis==2.0.0
pytest
-e .

View File

@@ -4,12 +4,12 @@ import sys
"""Setup script for the 'uncompyle6' distribution."""
SYS_VERSION = sys.version_info[0:2]
if not ((2, 6) <= SYS_VERSION <= (3, 7)) or ((3, 0) <= SYS_VERSION <= (3, 0)):
mess = "Python Release 2.6 .. 3.7 excluding 3.0 are supported in this 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])
elif SYS_VERSION < (2, 4) or (3, 0) <= SYS_VERSION:
elif SYS_VERSION < (2, 4):
mess += ("\nThis package is not supported for Python version %s."
% sys.version[0:3])
print(mess)

View File

@@ -1,5 +1,6 @@
PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clean_pyc nosetests \
check-bytecode-1.5 check-bytecode-1 check-bytecode-2 check-bytecode-3 \
check-bytecode-1 check-bytecode-1.3 check-bytecode-1.4 check-bytecode-1.5 \
check-bytecode-2 check-bytecode-3 check-bytecode-3-short \
check-bytecode-2.2 check-byteocde-2.3 check-bytecode-2.4 \
check-short check-2.6 check-2.7 check-3.0 check-3.1 check-3.2 check-3.3 \
check-3.4 check-3.5 check-3.6 check-3.7 check-5.6 5.6 5.8 \
@@ -21,7 +22,7 @@ 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:
@@ -33,6 +34,7 @@ check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-na
#: Run working tests from Python 3.0
check-3.0: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.0-run --verify-run
#: Run working tests from Python 3.1
check-3.1: check-bytecode
@@ -76,7 +78,7 @@ check-disasm:
$(PYTHON) dis-compare.py
#: Check deparsing bytecode 1.x only
check-bytecode-1: check-bytecode-1.5
check-bytecode-1: check-bytecode-1.4 check-bytecode-1.5
#: Check deparsing bytecode 2.x only
check-bytecode-2:
@@ -90,14 +92,35 @@ 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.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
#: Check deparsing bytecode 1.5 only
check-bytecode-1.5:
$(PYTHON) test_pythonlib.py --bytecode-1.5
@@ -202,6 +225,7 @@ check-bytecode-2.7:
#: 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:
@@ -263,7 +287,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.

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/emacs.pyc Normal file

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.

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.

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