Compare commits

..

112 Commits

Author SHA1 Message Date
rocky
1358d40aef Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-29 22:32:01 -04:00
rocky
c8c6f1a63d Merge hell 2021-10-29 22:29:15 -04:00
rocky
850500c7ad Merge branch 'master' into python-3.3-to-3.5 2021-10-29 22:25:36 -04:00
rocky
470d203b40 Merge branch 'master' of github.com:rocky/python-uncompyle6 2021-10-29 22:19:47 -04:00
rocky
4911d85237 Get ready for release 3.8.0 2021-10-29 22:17:17 -04:00
rocky
93a218a8b1 Get ready for release 3.8.0 2021-10-29 22:16:20 -04:00
rocky
9ec8918c1f Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-28 18:54:42 -04:00
rocky
08ed185608 Merge branch 'master' into python-3.3-to-3.5 2021-10-28 18:46:08 -04:00
rocky
32c4b84458 Better handling of bytecode errors 2021-10-28 18:37:54 -04:00
rocky
9cf345d446 Allow running test from 3.9 and 3.10
this does not mean we decompile either of those bytecode though.
2021-10-26 19:05:32 -04:00
rocky
c164df2795 Redo packaging. 2021-10-26 18:52:55 -04:00
rocky
3ad63071ac Admnistrivia: package info 2021-10-26 18:39:36 -04:00
rocky
25cd759dbe Packaging adminstrivia 2021-10-26 18:39:07 -04:00
rocky
000c060093 One more PYTHON_VERSION_TRIPLE conversion 2021-10-26 06:49:49 -04:00
rocky
39d79217ca Merge hell 2021-10-26 06:47:35 -04:00
rocky
5390e3b838 Merge hell 2021-10-26 06:42:23 -04:00
rocky
8aeb0aad8c Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-26 06:41:20 -04:00
rocky
d0ca7b0363 Loosen check to allow running from 2.4-3.10
We still only can *decompile* 2.4-3.8
2021-10-26 06:21:51 -04:00
rocky
a2e34ab75c Merge branch 'master' into python-3.3-to-3.5 2021-10-26 06:19:01 -04:00
rocky
5c2af69925 Loosen check to allow running from 2.4-3.10
We still only can *decompile* 2.4-3.8
2021-10-26 06:08:17 -04:00
rocky
8076c60eee Remove float2str 2021-10-26 04:43:08 -04:00
rocky
ea26084e6d Modernize and sync with decompyle3 better 2021-10-25 09:13:47 -04:00
rocky
1b4b6b334e Merge branch 'master' into python-3.3-to-3.5 2021-10-25 09:09:51 -04:00
rocky
482dbb5c82 Modernize and sync with decompyle3 better 2021-10-25 09:04:12 -04:00
rocky
fa203af665 Better messages when xdis not handling Python x.y 2021-10-24 23:22:41 -04:00
rocky
08e27a8b0f Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-24 01:54:39 -04:00
rocky
55ffaa1aff Merge branch 'master' into python-3.3-to-3.5 2021-10-24 01:52:52 -04:00
rocky
51ac72ba1f Sync with decompile3 2021-10-24 01:52:23 -04:00
rocky
d5bf7626af Fix bug in fragment parser 2021-10-24 01:32:41 -04:00
rocky
91fa73bf01 Try CI on 2.4 branch 2021-10-24 00:58:36 -04:00
rocky
9a1b77aff4 Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-24 00:49:32 -04:00
rocky
79d5790e3f Workflows CI adjusment 2021-10-23 16:06:02 -04:00
rocky
381a470d90 Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-23 16:02:28 -04:00
rocky
64b75625a9 Merge branch 'master' into python-3.3-to-3.5 2021-10-23 15:56:19 -04:00
rocky
7387e5094b More version tuple conversions 2021-10-23 15:54:14 -04:00
rocky
1bcd21a6f4 More version conversion bugs 2021-10-23 10:04:58 -04:00
rocky
536d45deb1 Another version bug 2021-10-23 10:01:53 -04:00
rocky
dce7e809e2 Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-23 09:52:40 -04:00
rocky
5ddbea73f4 Merge branch 'master' into python-3.3-to-3.5 2021-10-23 09:50:29 -04:00
rocky
71fe1e6c2c Fragment and other bugs
Part of the upgrade process
2021-10-23 09:47:30 -04:00
rocky
8a9a4ca6cc CircleCI testing 2021-10-23 09:12:45 -04:00
rocky
b8e9ce8a7a CircleCI testing 2021-10-23 09:08:11 -04:00
rocky
40a40b0bad Administrivia 2021-10-23 09:03:29 -04:00
rocky
528a2b0c22 Administrivia 2021-10-23 08:57:19 -04:00
rocky
d75bf1c32a Admnistrivia 2021-10-23 08:56:33 -04:00
rocky
cef3203601 merge hell 2021-10-23 08:46:05 -04:00
rocky
5b657ac7d8 Merge branch 'python-3.3-to-3.5' into python-2.4 2021-10-23 08:42:32 -04:00
rocky
1509bc4828 Administrivia 2021-10-23 08:35:00 -04:00
rocky
fad5089175 Administrivia 2021-10-23 08:33:39 -04:00
rocky
52262dc38a Merge hell 2021-10-23 08:27:47 -04:00
rocky
b61255535e Merge branch 'master' into python-3.3-to-3.5 2021-10-23 08:26:39 -04:00
rocky
e3369edaed DRY using version_info_to_str 2021-10-23 08:24:35 -04:00
rocky
ce58ed7434 CircleCI testing 2021-10-23 08:10:21 -04:00
rocky
01859ce820 Don not upgrade pip on older pythons 2021-10-23 07:53:50 -04:00
rocky
0a9dc57cc9 Try 3.6 CI testing 2021-10-21 18:53:44 -04:00
rocky
ada786e08c Administrivia 2021-10-21 16:38:12 -04:00
rocky
48bd832e7c Adminsitrivia 2021-10-21 16:36:46 -04:00
rocky
cfb5c442e2 Version twiddling 2021-10-21 16:33:42 -04:00
rocky
29a91fc015 Version twiddling 2021-10-21 16:33:09 -04:00
rocky
37f953c353 More version twiddling 2021-10-21 16:28:41 -04:00
rocky
4d84a723f4 Use right xdis branch 2021-10-21 16:22:57 -04:00
rocky
ddbfc168c5 CI testing 3.5, 3.6
Workflows doesn't go back before 3.5.
It is okay to use 3.6 in testing the 3.3-3.5 branch
2021-10-21 16:14:26 -04:00
rocky
a463220df2 Break out code for 3.3-3.5 versions 2021-10-21 16:12:39 -04:00
rocky
3e5f963c64 Merge branch 'master' of github.com:rocky/python-uncompyle6 2021-10-21 15:54:08 -04:00
R. Bernstein
c7ebdb344b Merge pull request #360 from rocky/3.10-adjust
Try Travis with new xdis
2021-10-21 15:52:40 -04:00
rocky
438c3b8d1d Add Windows and OSX CI 2021-10-21 15:47:15 -04:00
rocky
aa1d7abfdc Worflows CI testing 2021-10-21 15:42:22 -04:00
rocky
d3e30cf0e0 Update Python version in appveyor 2021-10-21 14:23:14 -04:00
rocky
36efc1fc8a Try 1st workflows CI 2021-10-21 14:18:56 -04:00
rocky
f00080317b Add pyston-2.3 in testing 2021-10-21 14:15:26 -04:00
rocky
162423895e Administrivia 2021-10-21 02:31:59 -04:00
rocky
f2750cff50 Correct pytest/test_grammar.py for new regime 2021-10-21 02:28:48 -04:00
R. Bernstein
256aaf0ef9 Update HOW-TO-REPORT-A-BUG.md 2021-10-21 02:23:03 -04:00
rocky
41314f95bb More Python version comparison adjustments 2021-10-19 16:30:56 -04:00
rocky
0645738775 Revise Python version comparisions
And set scanner.show_asm for 3.6
2021-10-19 05:54:54 -04:00
rocky
ceb7c659bd Python version comparison adjustments 2021-10-18 12:23:53 -04:00
rocky
8ac7a75372 Use tuples not floats in Python release comparison 2021-10-18 11:59:02 -04:00
rocky
15efaffe8d More Python version tuple comparison conversion 2021-10-16 11:41:22 -04:00
rocky
e8e006bb8c More Python version comparison conversions 2021-10-16 11:33:03 -04:00
rocky
c68b74a9c6 new dis - Python compisons involving tuples 2021-10-15 23:39:59 -04:00
rocky
f4bb0c44fe Try Travis with new xdis 2021-10-12 17:24:19 -04:00
R. Bernstein
8d81f4ab27 Update HOW-TO-REPORT-A-BUG.md 2021-09-16 06:23:25 -04:00
R. Bernstein
e0a56d4739 Update HOW-TO-REPORT-A-BUG.md 2021-09-03 07:41:38 -04:00
R. Bernstein
054364cb22 Update HOW-TO-REPORT-A-BUG.md 2021-09-03 07:32:53 -04:00
R. Bernstein
83c8313a8e Update HOW-TO-REPORT-A-BUG.md 2021-09-03 07:31:03 -04:00
rocky
184bda1b03 Work around broken modularity in python_parser 2021-08-27 02:13:09 -04:00
rocky
f374485e93 Another fragment fix for 3.8 2021-07-29 14:23:17 -04:00
rocky
fe7df87288 Sync 3.8 and Makefile changes with decompyle3
Makefile: pyston 2.3 tolerance
fragments: 3.8 comprehension adjustments
2021-07-29 13:16:03 -04:00
rocky
cfbb25df3d Fix some small bugs 2021-07-08 05:40:43 -04:00
rocky
d4174832a1 Black shouldn't format version.py 2021-06-23 11:46:31 -04:00
rocky
345de81d06 Administriva: add some config files
.editoryconfig: tell editors how to format this
setup.cfg: general project setup outside of Python-specific stuff
2021-06-23 02:09:32 -04:00
rocky
3684b38310 One more test 2021-06-15 22:50:36 -04:00
rocky
f472275196 Merge branch 'master' into python-2.4 2021-06-15 22:47:45 -04:00
rocky
2bca6753d3 Merge branch 'python-2.4' of github.com:rocky/python-uncompyle6 into python-2.4 2021-06-15 22:47:36 -04:00
R. Bernstein
dd8f22e698 Merge pull request #352 from rocky/lambda-bug
Fixes #360
2021-06-15 22:46:46 -04:00
rocky
96b1e435c2 Fixes #360 2021-06-15 22:42:55 -04:00
R. Bernstein
971757e997 Merge pull request #348 from IzeBerg/patch-1
'NoneType' object is not iterable with numproc > 1
2021-06-14 13:44:49 -04:00
Renat Iliev
2b154e0b88 'NoneType' object is not iterable with numproc > 1
main calls with source_paths=None, but it needs to be iterable
2021-06-14 19:50:18 +03:00
R. Bernstein
5d35a75743 Merge pull request #340 from timgates42/bugfix_typo_unnecessary
docs: fix simple typo, unecessary -> unnecessary
2020-12-31 03:22:09 -05:00
Tim Gates
fc38e23d8f docs: fix simple typo, unecessary -> unnecessary
There is a small typo in test/simple_source/looping/12_if_while_true_pass.py.

Should read `unnecessary` rather than `unecessary`.
2020-12-31 18:11:41 +11:00
rocky
5c16c73a6c Fix annotation transform for 3.7+
We were producing:

```
z: z: int = 5 on bytecode_3.7_run/02_var_annotate.pyc
```

because grammar went

     5. sstmt
        ann_assign (4) transformed by n_stmts: ('%|%[2]{attr}: %c\n', 0)
             0. ann_assign_init (3): ('%|%[2]{attr}: %c = %c\n', 0, 1)

The "ann_assign" added "z:". Instead we have now:

```
     5. sstmt
        ann_assign_init (3) transformed by n_stmts: ('%|%[2]{attr}: %c = %c\n', 0, 1)
```

Also, in the previous statement which appears in the listing (but is not
actually in the finaly tree) we had:

     4. sstmt
        assign (2): ('%|%c = %p\n', -1, (0, 200))
             0. expr
                L. L.   7        26  LOAD_CONST               5
             1. store

So we now preface the node type with "deleted", e.g.:

     4. deleted sstmt
        assign (2): ('%|%c = %p\n', -1, (0, 200))
             0. expr
                L. L.   7        26  LOAD_CONST               5
             1. store

to reduce confusion
2020-12-27 22:50:46 -05:00
R. Bernstein
3f665b939d Merge pull request #339 from bloerwald/parsers-fix_expr32_emitted_when_used_by_expr1024
parsers: parse2: fix: also emit expr32 if count perfectly divisible by 1024
2020-12-27 20:46:10 -05:00
Bernd Lörwald
f2f49104ea parsers: parse2: fix: also emit expr32 if count perfectly divisible by 1024
expr1024 requires expr32, but a build_count of 1024 would emit only the
expr1024 rule and rely on luck of it being emitted somewhere else.

Emit expr32 rule either if there is a expr32 use or a expr1024 use to avoid.
2020-12-28 01:42:42 +01:00
R. Bernstein
c2ee564e11 Update README.rst 2020-11-20 08:52:55 -05:00
rocky
f95db091bc Merge branch 'master' of github.com:rocky/python-uncompyle6 2020-11-03 18:09:40 -05:00
rocky
78dbc8ae0f Adjust ann_assign_init rule...
We've reduced spurious `sstmt` reductions. The `ann_assign_init` rule needs
to adjust accordingly.
2020-11-03 18:07:42 -05:00
rocky
a0cb9c5d6a merge hell 2020-10-31 11:37:58 -04:00
rocky
3ca66d0184 Merge branch 'master' into python-2.4 2020-10-31 11:37:53 -04:00
rocky
70b7e51df6 VERSION -> version 2020-10-31 11:25:06 -04:00
rocky
1164cd90dc Merge branch 'master' of github.com:rocky/python-uncompyle6 2020-09-10 17:19:36 -04:00
rocky
4bbdbe3894 Remove bogus async_with rule 2020-09-10 17:19:13 -04:00
rocky
28855767fb Get ready for release 3.7.4 2020-09-05 06:06:40 -04:00
107 changed files with 968 additions and 638 deletions

View File

@@ -1,4 +1,7 @@
version: 2
filters:
branches:
only: python-2.4
jobs:
build:
parallelism: 1
@@ -32,7 +35,12 @@ jobs:
- v2-dependencies-
- run:
command: sudo easy_install xdis spark-parser && sudo pip install -e . && sudo pip install -r requirements-dev.txt
command: | # Use pip to install dependengcies
sudo easy_install click==7.1.2
# Until next release use github xdis
sudo pip install git+git://github.com/rocky/python-xdis.git@python-2.4-to-2.7#egg=xdis
sudo pip install -e .
sudo pip install -r requirements-dev.txt
# Save dependency cache
- save_cache:

28
.editorconfig Normal file
View File

@@ -0,0 +1,28 @@
# THis is an EditorConfig file
# https://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
# Tab indentation (no size specified)
[Makefile]
indent_style = tab

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

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

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

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

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

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

View File

@@ -4,6 +4,8 @@ python:
- 2.7 # this is a cheat here because travis doesn't do 2.4-2.6
install:
# Remove the next line when xdis 6.0.0 is released
# - pip install git://github.com/rocky/python-xdis.git#egg=xdis
- pip install -e .
- pip install -r requirements-dev.txt

View File

@@ -20,14 +20,9 @@
This decompiler is a constant work in progress: Python keeps
changing, and so does its code generation.
There is no Python decompiler yet that I know about that will
decompile everything. Overall, I think this one probably does the best
job of *any* Python decompiler that handles such a wide range of
versions.
There is no Python decompiler yet that I know about that will decompile everything. Overall, I think this one probably does the best job of *any* Python decompiler that handles such a wide range of versions.
But at any given time, there are a number of valid Python bytecode
files that I know of that will cause problems. See, for example, the
list in
But at any given time, there are a number of valid Python bytecode files that I know of that will cause problems. See, for example, the list in
[`test/stdlib/runtests.sh`](https://github.com/rocky/python-uncompyle6/blob/master/test/stdlib/runtests.sh).
There are far more bug reporters than there are bug fixers.
@@ -40,7 +35,7 @@ Unless you are a sponsor of this project, it may take a while, maybe a week or s
## Do you have valid bytecode?
As mentioned in README.rst, this project doesn't handle obfuscated
code. See README.rst for suggestions for how to remove some kinds of
code or release candidates. See README.rst for suggestions for how to remove some kinds of
obfuscation.
Checking if bytecode is valid is pretty simple: disassemble the code.
@@ -122,8 +117,7 @@ if False:
Python will eliminate the entire "if" statement.
So just because the text isn't the same, does not
necessarily mean there's a bug.
So just because the text isn't the same, this does not necessarily mean there's a bug.
# What to send (minimum requirements)
@@ -154,26 +148,18 @@ provide the input command and the output from that, please give:
## But I don't *have* the source code!
There is Python assembly code on parse errors, so simply by hand decompile that. To get a full disassembly, use `pydisasm` from the [xdis](https://pypi.python.org/pypi/xdis) package. Opcodes are described in the documentation for the[dis](https://docs.python.org/3.6/library/dis.html) module.
There is Python assembly code on parse errors, so simply by hand decompile that. To get a full disassembly, use `pydisasm` from the [xdis](https://pypi.python.org/pypi/xdis) package. Opcodes are described in the documentation for the [dis](https://docs.python.org/3.6/library/dis.html) module.
### But I don't *have* the source code and am incapable of figuring how to do a hand disassembly!
Well, you could learn. No one is born into this world knowing how to
disassemble Python bytecode. And as Richard Feynman once said, "What
one fool can learn, so can another."
Well, you could learn. No one is born into this world knowing how to disassemble Python bytecode. And as Richard Feynman once said, "What one fool can learn, so can another."
If this is too difficult, or too time consuming, or not of interest to
you, then you might consider sponsoring the project. [Crazy
Compilers](http://www.crazy-compilers.com/decompyle/) offers a
byte-code decompiler service for versions of Python up to 2.6. (If
there are others around let me know and I'll list them here.)
If this is too difficult, or too time consuming, or not of interest to you, then you might consider [sponsoring](https://github.com/sponsors/rocky) the project. [Crazy
Compilers](http://www.crazy-compilers.com/decompyle/) offers a byte-code decompiler service for versions of Python up to 2.6. (If there are others around let me know and I'll list them here.)
# Narrowing the problem
I don't need or want the entire source code base for the file(s) or
module(s) can't be decompiled. I just need those file(s) or module(s).
If there are problems in several files, file a bug report for each
file.
I don't need or want the entire source code base for the file(s) or module(s) can't be decompiled. I just need those file(s) or module(s). If there are problems in several files, file a bug report for each file.
Python modules can get quite large, and usually decompilation problems
occur in a single function or maybe the main-line code but not any of

11
NEWS.md
View File

@@ -1,3 +1,14 @@
3.8.0: 2020-10-29
=================
* Better handling of invalid bytecode magic
* Support running from 3.9 and 3.10 although we do not support those bytecodes
* Redo version comparisons using tuples instead of floats. This is needed for Python 3.10
* Split out into 3 branches so that the master branch can assume Python 3.6+ conventions, especially type annotations
* Source Fragment fixes
* Lambda-bug fixes #360
* Bug fixes
3.7.4: 2020-8-05
================

View File

@@ -240,7 +240,7 @@ be solved if one were to put in the time to do so. The problem is that
there aren't that many people who have been working on bug fixing.
Some of the bugs in 3.7 and 3.8 are simply a matter of back-porting
the fixes in decmopyle3.
the fixes in decompyle3.
You may run across a bug, that you want to report. Please do so. But
be aware that it might not get my attention for a while. If you

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018, 2020 Rocky Bernstein <rocky@gnu.org>
# Copyright (C) 2018, 2020-2021 Rocky Bernstein <rocky@gnu.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
# Things that change more often go here.
copyright = """
Copyright (C) 2015-2020 Rocky Bernstein <rb@dustyfeet.com>.
Copyright (C) 2015-2021 Rocky Bernstein <rb@dustyfeet.com>.
"""
classifiers = [
@@ -43,10 +43,12 @@ classifiers = [
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.4",
"Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.0",
"Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.2",
@@ -56,6 +58,9 @@ classifiers = [
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Debuggers",
"Topic :: Software Development :: Libraries :: Python Modules",
]
@@ -70,7 +75,7 @@ entry_points = {
]
}
ftp_url = None
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 5.0.4, <5.1.0"]
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 6.0.2, < 6.1.0"]
license = "GPL3"
mailing_list = "python-debugger@googlegroups.com"
@@ -98,6 +103,6 @@ def read(*rnames):
return open(os.path.join(srcdir, *rnames)).read()
# Get info from files; set: long_description and VERSION
# Get info from files; set: long_description and __version__
long_description = read("README.rst") + "\n"
exec(read("uncompyle6/version.py"))

View File

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

View File

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

View File

@@ -57,9 +57,9 @@
$ . ./admin-tools/make-dist-older.sh
$ pyenv local 3.8.5
$ twine check dist/uncompyle6-$VERSION*
$ git tag release-python-2.4-$VERSION
$ ./admin-tools/make-dist-newer.sh
$ twine check dist/uncompyle6-$VERSION*
$ git tag release-python-2.4-$VERSION
# Check package on github

View File

@@ -9,7 +9,7 @@ owd=$(pwd)
trap finish EXIT
cd $(dirname ${BASH_SOURCE[0]})
if ! source ./pyenv-older-versions ; then
if ! source ./pyenv-2.4-2.7-versions ; then
exit $?
fi
if ! source ./setup-python-2.4.sh ; then
@@ -18,7 +18,7 @@ fi
cd ..
source $PACKAGE/version.py
echo $VERSION
echo $__version__
for pyversion in $PYVERSIONS; do
if ! pyenv local $pyversion ; then
@@ -29,11 +29,15 @@ for pyversion in $PYVERSIONS; do
python setup.py bdist_egg
done
pyenv local 2.7.18
python setup.py bdist_wheel
mv -v dist/${PACKAGE}-$__version__-py2{.py3,}-none-any.whl
# Pypi can only have one source tarball.
# Tarballs can get created from the above setup, so make sure to remove them since we want
# the tarball from master.
tarball=dist/${PACKAGE}-$VERSION-tar.gz
tarball=dist/${PACKAGE}-${__version_}_-tar.gz
if [[ -f $tarball ]]; then
rm -v dist/${PACKAGE}-$VERSION-tar.gz
rm -v dist/${PACKAGE}-${__version__}-tar.gz
fi

View File

@@ -0,0 +1,38 @@
#!/bin/bash
PACKAGE=uncompyle6
# FIXME put some of the below in a common routine
function finish {
cd $owd
}
cd $(dirname ${BASH_SOURCE[0]})
owd=$(pwd)
trap finish EXIT
if ! source ./pyenv-3.3-3.5-versions ; then
exit $?
fi
if ! source ./setup-python-3.3.sh ; then
exit $?
fi
cd ..
source $PACKAGE/version.py
echo $__version__
for pyversion in $PYVERSIONS; do
if ! pyenv local $pyversion ; then
exit $?
fi
# pip bdist_egg create too-general wheels. So
# we narrow that by moving the generated wheel.
# Pick out first two number of version, e.g. 3.5.1 -> 35
first_two=$(echo $pyversion | cut -d'.' -f 1-2 | sed -e 's/\.//')
rm -fr build
python setup.py bdist_egg bdist_wheel
mv -v dist/${PACKAGE}-$__version__-{py2.py3,py$first_two}-none-any.whl
done
python ./setup.py sdist

View File

@@ -10,7 +10,7 @@ cd $(dirname ${BASH_SOURCE[0]})
owd=$(pwd)
trap finish EXIT
if ! source ./pyenv-newer-versions ; then
if ! source ./pyenv-newest-versions ; then
exit $?
fi
if ! source ./setup-master.sh ; then
@@ -19,7 +19,7 @@ fi
cd ..
source $PACKAGE/version.py
echo $VERSION
echo $__version__
for pyversion in $PYVERSIONS; do
if ! pyenv local $pyversion ; then
@@ -32,7 +32,7 @@ for pyversion in $PYVERSIONS; do
first_two=$(echo $pyversion | cut -d'.' -f 1-2 | sed -e 's/\.//')
rm -fr build
python setup.py bdist_egg bdist_wheel
mv -v dist/${PACKAGE}-$VERSION-{py2.py3,py$first_two}-none-any.whl
mv -v dist/${PACKAGE}-$__version__-{py2.py3,py$first_two}-none-any.whl
done
python ./setup.py sdist

View File

@@ -0,0 +1,9 @@
# -*- shell-script -*-
# Sets PYVERSIONS to be pyenv versions that
# we can use in the python-2.4-to-2.7 branch.
if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS='2.4.6 2.5.6 2.6.9 2.7.18'

View File

@@ -6,4 +6,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS='2.4.6 2.5.6'
export PYVERSIONS='3.1.5 3.2.6'

View File

@@ -0,0 +1,8 @@
# -*- shell-script -*-
# Sets PYVERSIONS to be pyenv versions that
# we can use in the python-3.3-to-3.5 branch.
if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS=' 3.3.7 3.4.10 3.5.10 '

View File

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

View File

@@ -0,0 +1,8 @@
# -*- shell-script -*-
# Sets PYVERSIONS to be pyenv versions that
# we can use in the master branch.
if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash"
exit 1
fi
export PYVERSIONS='3.7.11 3.8.12 3.9.7 3.10.0'

View File

@@ -1,5 +1,5 @@
#!/bin/bash
PYTHON_VERSION=3.7.7
PYTHON_VERSION=3.7.12
# FIXME put some of the below in a common routine
function finish {
@@ -20,4 +20,4 @@ cd $fulldir/..
(cd ../python-xdis && git checkout master && pyenv local $PYTHON_VERSION) && git pull && \
git checkout master && pyenv local $PYTHON_VERSION && git pull
cd $owd
rm -v */.python-version || true
rm -v */.python-version >/dev/null 2>&1 || true

View File

@@ -11,7 +11,7 @@ mydir=$(dirname $bs)
fulldir=$(readlink -f $mydir)
cd $fulldir/..
(cd ../python-spark && git checkout python-2.4 && pyenv local $PYTHON_VERSION) && git pull && \
(cd ../python-xdis && git checkout python-2.4 && pyenv local $PYTHON_VERSION) && git pull && \
(cd ../python-xdis && . ./admin-tools/setup-python-2.4.sh) && \
git checkout python-2.4 && pyenv local $PYTHON_VERSION && git pull
cd $owd
rm -v */.python-version || true

View File

@@ -0,0 +1,15 @@
#!/bin/bash
PYTHON_VERSION=3.3.7
pyenv local $PYTHON_VERSION
owd=$(pwd)
bs=${BASH_SOURCE[0]}
mydir=$(dirname $bs)
fulldir=$(readlink -f $mydir)
cd $fulldir/..
(cd ../python-xdis && ./admin-tools/setup-python-3.3.sh)
cd $owd
rm -v */.python-version || true
git checkout python-3.3-to-3.5 && git pull && pyenv local $PYTHON_VERSION

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
from uncompyle6 import PYTHON_VERSION, code_deparse
from uncompyle6 import code_deparse
from xdis.version_info import PYTHON_VERSION_TRIPLE
if PYTHON_VERSION > 2.6:
if PYTHON_VERSION_TRIPLE >= (2, 7):
def test_single_mode():
single_expressions = (
'i = 1',

View File

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

View File

@@ -1,11 +1,57 @@
[bdist_rpm]
release = 1
packager = rocky <rb@dustyfeet.com>
doc_files = README
release = 0
packager = rocky <rb@dustyfeet.com
doc_files = README.rst
ChangeLog
COPYING
DECOMPYLE-2.4-CHANGELOG.txt
HISTORY.md
HOW-TO_REPORT-A-BUG.md
NEWS.md
# doc/
# examples/
[bdist_wheel]
universal=1
[egg_info]
tag_build =
tag_date = 0
[metadata]
description_file = README.rst
[flake8]
# max-line-length setting: NO we do not want everyone writing 120-character lines!
# We are setting the maximum line length big here because there are longer
# lines allowed by black in some cases that are forbidden by flake8. Since
# black has the final say about code formatting issues, this setting is here to
# make sure that flake8 doesn't fail the build on longer lines allowed by
# black.
max-line-length = 120
max-complexity = 12
select = E,F,W,C,B,B9
ignore =
# E123 closing bracket does not match indentation of opening bracket's line
E123
# E203 whitespace before ':' (Not PEP8 compliant, Python Black)
E203
# E501 line too long (82 > 79 characters) (replaced by B950 from flake8-bugbear,
# https://github.com/PyCQA/flake8-bugbear)
E501
# W503 line break before binary operator (Not PEP8 compliant, Python Black)
W503
# W504 line break after binary operator (Not PEP8 compliant, Python Black)
W504
# C901 function too complex - since many of zz9 functions are too complex with a lot
# of if branching
C901
# module level import not at top of file. This is too restrictive. Can't even have a
# docstring higher.
E402
per-file-ignores =
# These are config files. The `c` variable them is injected not defined.
pow/ansible/roles/jupyterhub/templates/jupyterhub_config*.py:F821
# Ignore some errors in files that are stolen from other projects to avoid lots
# of merge problems later .
pow/ansible/roles/webtier/files/supervisor_httpgroupok.py:E126,E128,E222,E225,E226,E261,E301,E302,E305,F841,E201,E202
silhouette/src/silhouette/gprof2dot.py:E711,E713,E741,F401
# Ignore undefined name errors in "expectation" test Python code.
# These files get exec'd in an environment that defines the variables.
server/tests/files/expectations/*.py:F821

View File

@@ -6,12 +6,18 @@ import sys
SYS_VERSION = sys.version_info[0:2]
if not ((2, 4) <= SYS_VERSION <= (2, 7)):
mess = "Python Release 2.4 .. 2.7 are supported in this code branch."
if ((3, 2) <= SYS_VERSION <= (3, 8)):
if ((3, 6) <= SYS_VERSION < (3, 9)):
mess += ("\nFor your Python, version %s, use the master code/branch." %
sys.version[0:3])
else:
mess += ("\nThis package is not supported before Python 2.4. Your Python version is %s."
% sys.version[0:3])
elif (3, 3) <= SYS_VERSION <= (3, 6):
mess += (
"\nFor your Python, version %s, use the python-3.3-3.5 code/branch."
% sys.version[0:3]
)
elif SYS_VERSION < (2, 4):
mess += (
"\nThis package is not supported for Python before Python 2.4 version %s." % sys.version[0:3]
)
print(mess)
raise Exception(mess)
@@ -26,7 +32,7 @@ from __pkginfo__ import (
modname,
py_modules,
short_desc,
VERSION,
__version__,
web,
zip_safe,
)
@@ -49,6 +55,6 @@ setup(
test_suite="nose.collector",
url=web,
tests_require=["nose>=1.0"],
version=VERSION,
version=__version__,
zip_safe=zip_safe,
)

View File

@@ -13,7 +13,7 @@ PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clea
GIT2CL ?= git2cl
PYTHON ?= python
PYTHON_VERSION = $(shell $(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2)
PYTHON_VERSION = $(shell $(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2 | head -1)
NATIVE_CHECK = check-$(PYTHON_VERSION)
# Set COMPILE='--compile' to force compilation before check
@@ -22,8 +22,8 @@ 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-$${PYTHON_VERSION}
@$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2` | head -1; \
$(MAKE) check-bytecode-${PYTHON_VERSION}
# Run all tests
check:
@@ -77,6 +77,12 @@ check-3.7: check-bytecode
# $(PYTHON) test_pythonlib.py --bytecode-3.8-run --verify-run
# $(PYTHON) test_pythonlib.py --bytecode-3.8 --syntax-verify $(COMPILE)
check-3.9: check-bytecode
@echo "Note that we do not support decompiling Python 3.9 bytecode - no 3.9 tests run"
check-3.10: check-bytecode
@echo "Note that we do not support decompiling Python 3.10 bytecode - no 3.10 tests run"
# FIXME
#: this is called when running under pypy3.5-5.8.0, pypy2-5.6.0, or pypy3.6-7.3.0
5.8 5.6:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
# From https://github.com/rocky/python-uncompyle6/issues/350
# This is RUNNABLE!
a = (lambda x: x)(abs)
assert a(-3) == 3

View File

@@ -1,5 +1,5 @@
# Python 3.3 pyclbr.py
# Note that Python 3 adds a lot of unecessary "continues"
# Note that Python 3 adds a lot of unnecessary "continues"
# and puts that in for "pass"
def _readmodule(g, token, path):
for tokentype in g:

View File

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

View File

@@ -30,10 +30,7 @@ import sys
__docformat__ = "restructuredtext"
from uncompyle6.version import VERSION
# This ensures VERSION will appear in pydoc
__version__ = VERSION
from uncompyle6.version import __version__
PYTHON3 = sys.version_info >= (3, 0)

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python
# Mode: -*- python -*-
#
# Copyright (c) 2015-2016, 2018 by Rocky Bernstein <rb@dustyfeet.com>
# Copyright (c) 2015-2016, 2018, 2020 by Rocky Bernstein <rb@dustyfeet.com>
#
import sys, os, getopt
from uncompyle6.disas import disassemble_file
from uncompyle6.version import VERSION
from uncompyle6.version import __version__
program, ext = os.path.splitext(os.path.basename(__file__))
@@ -57,7 +57,7 @@ Type -h for for full help.""" % program
print(__doc__)
sys.exit(1)
elif opt in ('-V', '--version'):
print("%s %s" % (program, VERSION))
print("%s %s" % (program, __version__))
sys.exit(0)
else:
print(opt)

View File

@@ -1,10 +1,11 @@
#!/usr/bin/env python
# Mode: -*- python -*-
#
# Copyright (c) 2015-2017, 2019 by Rocky Bernstein
# Copyright (c) 2015-2017, 2019-2020 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
import sys, os, getopt, time
from xdis.version_info import version_tuple_to_str
program = 'uncompyle6'
@@ -62,7 +63,7 @@ program = 'uncompyle6'
from uncompyle6 import verify
from uncompyle6.main import main, status_msg
from uncompyle6.version import VERSION
from uncompyle6.version import __version__
def usage():
print(__doc__)
@@ -70,9 +71,12 @@ def usage():
def main_bin():
if not (sys.version_info[0:2] in ((2, 4), (2, 5), (2, 6), (2, 7))):
sys.stderr.write('Error: this branch of %s requires Python 2.4, 2.5, 2.6 or 2.7'
% program)
if not (sys.version_info[0:2] in ((2, 4), (2, 5), (2, 6), (2, 7), (3, 0),
(3, 1), (3, 2), (3, 3),
(3, 4), (3, 5), (3, 6),
(3, 7), (3, 8), (3, 9), (3, 10)
)):
print('Error: %s requires Python 2.4-3.10' % program)
sys.exit(-1)
do_verify = recurse_dirs = False
@@ -101,7 +105,7 @@ def main_bin():
print(__doc__)
sys.exit(0)
elif opt in ('-V', '--version'):
print("%s %s" % (program, VERSION))
print("%s %s" % (program, __version__))
sys.exit(0)
elif opt == '--verify':
options['do_verify'] = 'strong'
@@ -193,6 +197,9 @@ def main_bin():
mess = status_msg(do_verify, *result)
print('# ' + mess)
pass
except ImportError:
print(str(sys.exc_info()[1]))
sys.exit(2)
except (KeyboardInterrupt):
pass
except verify.VerifyCmpError:
@@ -221,7 +228,7 @@ def main_bin():
if f is None:
break
(t, o, f, v) = \
main(src_base, out_base, [f], None, outfile, **options)
main(src_base, out_base, [f], [], outfile, **options)
tot_files += t
okay_files += o
failed_files += f

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2016, 2818-2020 by Rocky Bernstein
# Copyright (c) 2015-2016, 2818-2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@@ -33,6 +33,7 @@ import sys
from collections import deque
from xdis import check_object_path, iscode, load_module
from xdis.version_info import version_tuple_to_str
from uncompyle6.scanner import get_scanner
@@ -45,7 +46,7 @@ def disco(version, co, out=None, is_pypy=False):
# store final output stream for case of error
real_out = out or sys.stdout
real_out.write("# Python %s\n" % version)
real_out.write("# Python %s\n" % version_tuple_to_str(version))
if co.co_filename:
real_out.write("# Embedded file name: %s\n" % co.co_filename)

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018-2020 Rocky Bernstein <rocky@gnu.org>
# Copyright (C) 2018-2021 Rocky Bernstein <rocky@gnu.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -14,12 +14,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime, os, subprocess, sys
from uncompyle6 import verify, IS_PYPY, PYTHON_VERSION
from xdis import iscode, sysinfo2float
from uncompyle6 import verify
from xdis import iscode
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, version_tuple_to_str
from uncompyle6.disas import check_object_path
from uncompyle6.semantics import pysource
from uncompyle6.parser import ParserError
from uncompyle6.version import VERSION
from uncompyle6.version import __version__
# from uncompyle6.linenumbers import line_number_mapping
@@ -29,7 +30,6 @@ from uncompyle6.semantics.linemap import deparse_code_with_map
from xdis.load import load_module
def _get_outstream(outfile):
dir = os.path.dirname(outfile)
failed_file = outfile + "_failed"
@@ -66,7 +66,7 @@ def decompile(
Caller is responsible for closing `out` and `mapstream`
"""
if bytecode_version is None:
bytecode_version = sysinfo2float()
bytecode_version = PYTHON_VERSION_TRIPLE
# store final output stream for case of error
real_out = out or sys.stdout
@@ -75,7 +75,7 @@ def decompile(
s += "\n"
real_out.write(s)
assert iscode(co)
assert iscode(co), ("%s does not smell like code" % co)
if is_pypy:
co_pypy_str = "PyPy "
@@ -97,20 +97,21 @@ def decompile(
write("# -*- coding: %s -*-" % source_encoding)
write(
"# uncompyle6 version %s\n"
"# %sPython bytecode %s%s\n# Decompiled from: %sPython %s" %
(VERSION,
co_pypy_str,
bytecode_version,
"# %sPython bytecode %s%s\n# Decompiled from: %sPython %s"
% (
__version__,
co_pypy_str,
version_tuple_to_str(bytecode_version),
" (%s)" % m, run_pypy_str,
"\n# ".join(sys_version_lines),
)
"\n# ".join(sys_version_lines),
)
)
if bytecode_version >= 3.0:
write(
"# Warning: this version of Python has problems handling the Python 3 byte type in constants properly.\n"
)
if co.co_filename:
write("# Embedded file name: %s" % co.co_filename,)
write("# Embedded file name: %s" % co.co_filename)
if timestamp:
write("# Compiled at: %s" %
datetime.datetime.fromtimestamp(timestamp))
@@ -161,9 +162,9 @@ def compile_file(source_path):
basename = source_path
if hasattr(sys, "pypy_version_info"):
bytecode_path = "%s-pypy%s.pyc" % (basename, PYTHON_VERSION)
bytecode_path = "%s-pypy%s.pyc" % (basename, version_tuple_to_str())
else:
bytecode_path = "%s-%s.pyc" % (basename, PYTHON_VERSION)
bytecode_path = "%s-%s.pyc" % (basename, version_tuple_to_str())
print("compiling %s to %s" % (source_path, bytecode_path))
py_compile.compile(source_path, bytecode_path, "exec")
@@ -297,7 +298,7 @@ def main(
else:
buffering = 0
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering)
if PYTHON_VERSION > 2.6:
if PYTHON_VERSION_TRIPLE > (2, 6):
tee = subprocess.Popen(["tee", current_outfile],
stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 Rocky Bernstein
# Copyright (c) 2015-2021 Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@@ -21,9 +21,9 @@ Common uncompyle6 parser routines.
import sys
from xdis import iscode, py_str2float
from spark_parser import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.show import maybe_show_asm
from xdis import iscode
class ParserError(Exception):
@@ -640,107 +640,109 @@ def get_python_parser(
# If version is a string, turn that into the corresponding float.
if isinstance(version, str):
version = py_str2float(version)
version = tuple([int(v) for v in version.split(".")[:2]])
version = version[:2]
# FIXME: there has to be a better way...
# We could do this as a table lookup, but that would force us
# in import all of the parsers all of the time. Perhaps there is
# a lazy way of doing the import?
if version < 3.0:
if version < 2.2:
if version == 1.0:
if version < (3, 0):
if version < (2, 2):
if version == (1, 0):
import uncompyle6.parsers.parse10 as parse10
if compile_mode == "exec":
p = parse10.Python10Parser(debug_parser)
else:
p = parse10.Python01ParserSingle(debug_parser)
elif version == 1.1:
elif version == (1, 1):
import uncompyle6.parsers.parse11 as parse11
if compile_mode == "exec":
p = parse11.Python11Parser(debug_parser)
else:
p = parse11.Python11ParserSingle(debug_parser)
if version == 1.2:
if version == (1, 2):
import uncompyle6.parsers.parse12 as parse12
if compile_mode == "exec":
p = parse12.Python12Parser(debug_parser)
else:
p = parse12.Python12ParserSingle(debug_parser)
if version == 1.3:
if version == (1, 3):
import uncompyle6.parsers.parse13 as parse13
if compile_mode == "exec":
p = parse13.Python13Parser(debug_parser)
else:
p = parse13.Python13ParserSingle(debug_parser)
elif version == 1.4:
elif version == (1, 4):
import uncompyle6.parsers.parse14 as parse14
if compile_mode == "exec":
p = parse14.Python14Parser(debug_parser)
else:
p = parse14.Python14ParserSingle(debug_parser)
elif version == 1.5:
elif version == (1, 5):
import uncompyle6.parsers.parse15 as parse15
if compile_mode == "exec":
p = parse15.Python15Parser(debug_parser)
else:
p = parse15.Python15ParserSingle(debug_parser)
elif version == 1.6:
elif version == (1, 6):
import uncompyle6.parsers.parse16 as parse16
if compile_mode == "exec":
p = parse16.Python16Parser(debug_parser)
else:
p = parse16.Python16ParserSingle(debug_parser)
elif version == 2.1:
elif version == (2, 1):
import uncompyle6.parsers.parse21 as parse21
if compile_mode == "exec":
p = parse21.Python21Parser(debug_parser)
else:
p = parse21.Python21ParserSingle(debug_parser)
elif version == 2.2:
elif version == (2, 2):
import uncompyle6.parsers.parse22 as parse22
if compile_mode == "exec":
p = parse22.Python22Parser(debug_parser)
else:
p = parse22.Python22ParserSingle(debug_parser)
elif version == 2.3:
elif version == (2, 3):
import uncompyle6.parsers.parse23 as parse23
if compile_mode == "exec":
p = parse23.Python23Parser(debug_parser)
else:
p = parse23.Python23ParserSingle(debug_parser)
elif version == 2.4:
elif version == (2, 4):
import uncompyle6.parsers.parse24 as parse24
if compile_mode == "exec":
p = parse24.Python24Parser(debug_parser)
else:
p = parse24.Python24ParserSingle(debug_parser)
elif version == 2.5:
elif version == (2, 5):
import uncompyle6.parsers.parse25 as parse25
if compile_mode == "exec":
p = parse25.Python25Parser(debug_parser)
else:
p = parse25.Python25ParserSingle(debug_parser)
elif version == 2.6:
elif version == (2, 6):
import uncompyle6.parsers.parse26 as parse26
if compile_mode == "exec":
p = parse26.Python26Parser(debug_parser)
else:
p = parse26.Python26ParserSingle(debug_parser)
elif version == 2.7:
elif version == (2, 7):
import uncompyle6.parsers.parse27 as parse27
if compile_mode == "exec":
@@ -760,63 +762,63 @@ def get_python_parser(
else:
import uncompyle6.parsers.parse3 as parse3
if version == 3.0:
if version == (3, 0):
import uncompyle6.parsers.parse30 as parse30
if compile_mode == "exec":
p = parse30.Python30Parser(debug_parser)
else:
p = parse30.Python30ParserSingle(debug_parser)
elif version == 3.1:
elif version == (3, 1):
import uncompyle6.parsers.parse31 as parse31
if compile_mode == "exec":
p = parse31.Python31Parser(debug_parser)
else:
p = parse31.Python31ParserSingle(debug_parser)
elif version == 3.2:
elif version == (3, 2):
import uncompyle6.parsers.parse32 as parse32
if compile_mode == "exec":
p = parse32.Python32Parser(debug_parser)
else:
p = parse32.Python32ParserSingle(debug_parser)
elif version == 3.3:
elif version == (3, 3):
import uncompyle6.parsers.parse33 as parse33
if compile_mode == "exec":
p = parse33.Python33Parser(debug_parser)
else:
p = parse33.Python33ParserSingle(debug_parser)
elif version == 3.4:
elif version == (3, 4):
import uncompyle6.parsers.parse34 as parse34
if compile_mode == "exec":
p = parse34.Python34Parser(debug_parser)
else:
p = parse34.Python34ParserSingle(debug_parser)
elif version == 3.5:
elif version == (3, 5):
import uncompyle6.parsers.parse35 as parse35
if compile_mode == "exec":
p = parse35.Python35Parser(debug_parser)
else:
p = parse35.Python35ParserSingle(debug_parser)
elif version == 3.6:
elif version == (3, 6):
import uncompyle6.parsers.parse36 as parse36
if compile_mode == "exec":
p = parse36.Python36Parser(debug_parser)
else:
p = parse36.Python36ParserSingle(debug_parser)
elif version == 3.7:
elif version == (3, 7):
import uncompyle6.parsers.parse37 as parse37
if compile_mode == "exec":
p = parse37.Python37Parser(debug_parser)
else:
p = parse37.Python37ParserSingle(debug_parser)
elif version == 3.8:
elif version == (3, 8):
import uncompyle6.parsers.parse38 as parse38
if compile_mode == "exec":
@@ -875,16 +877,23 @@ def python_parser(
# For heavy grammar debugging
# parser_debug = {'rules': True, 'transition': True, 'reduce' : True,
# 'showstack': 'full'}
p = get_python_parser(version, parser_debug)
# FIXME: have p.insts update in a better way
# modularity is broken here
p.insts = scanner.insts
p.offset2inst_index = scanner.offset2inst_index
return parse(p, tokens, customize, co)
if __name__ == "__main__":
def parse_test(co):
from uncompyle6 import PYTHON_VERSION, IS_PYPY
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
ast = python_parser(PYTHON_VERSION, co, showasm=True, is_pypy=IS_PYPY)
ast = python_parser(PYTHON_VERSION_TRIPLE[:2], co, showasm=True, is_pypy=IS_PYPY)
print(ast)
return
parse_test(parse_test.func_code)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2020 Rocky Bernstein
# Copyright (c) 2015-2021 Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
# Copyright (c) 1999 John Aycock
@@ -317,7 +317,7 @@ class Python2Parser(PythonParser):
build_count = token.attr
thousands = build_count // 1024
thirty32s = (build_count // 32) % 32
if thirty32s > 0:
if thirty32s > 0 or thousands > 0:
rule = "expr32 ::=%s" % (" expr" * 32)
self.add_unique_rule(rule, opname_base, build_count, customize)
if thousands > 0:
@@ -348,7 +348,7 @@ class Python2Parser(PythonParser):
],
customize,
)
if self.version >= 2.7:
if self.version >= (2, 7):
self.add_unique_rule(
"dict_comp_func ::= BUILD_MAP_n LOAD_FAST FOR_ITER store "
"comp_iter JUMP_BACK RETURN_VALUE RETURN_LAST",
@@ -575,7 +575,7 @@ class Python2Parser(PythonParser):
customize,
)
if self.version >= 2.7:
if self.version >= (2, 7):
if i > 0:
prev_tok = tokens[i - 1]
if prev_tok == "LOAD_DICTCOMP":

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016, 2018-2020 by Rocky Bernstein
# Copyright (c) 2016, 2018-2021 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@@ -24,47 +24,56 @@ scanners, e.g. for Python 2.7 or 3.4.
from array import array
import sys
from uncompyle6 import PYTHON3, IS_PYPY, PYTHON_VERSION
from uncompyle6.scanners.tok import Token
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, PYTHON3, version_tuple_to_str
import xdis
from xdis import Bytecode, canonic_python_version, code2num, instruction_size, extended_arg_val, next_offset
from xdis import (
Bytecode,
canonic_python_version,
code2num,
instruction_size,
extended_arg_val,
next_offset,
)
if PYTHON_VERSION < 2.6:
if PYTHON_VERSION_TRIPLE < (2, 6):
from xdis.namedtuple24 import namedtuple
else:
from collections import namedtuple
# The byte code versions we support.
# Note: these all have to be floats
# Note: these all have to be tuples of 2 ints
PYTHON_VERSIONS = frozenset(
(
1.0,
1.1,
1.3,
1.4,
1.5,
1.6,
2.1,
2.2,
2.3,
2.4,
2.5,
2.6,
2.7,
3.0,
3.1,
3.2,
3.3,
3.4,
3.5,
3.6,
3.7,
3.8,
3.9,
(1, 0),
(1, 1),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(2, 1),
(2, 2),
(2, 3),
(2, 4),
(2, 5),
(2, 6),
(2, 7),
(3, 0),
(3, 1),
(3, 2),
(3, 3),
(3, 4),
(3, 5),
(3, 6),
(3, 7),
(3, 8),
)
)
CANONIC2VERSION = dict((canonic_python_version[str(v)], v) for v in PYTHON_VERSIONS)
CANONIC2VERSION = dict(
(canonic_python_version[version_tuple_to_str(python_version)], python_version)
for python_version in PYTHON_VERSIONS
)
# Magic changed mid version for Python 3.5.2. Compatibility was added for
# the older 3.5 interpreter magic.
@@ -105,15 +114,19 @@ class Scanner(object):
self.show_asm = show_asm
self.is_pypy = is_pypy
if version in PYTHON_VERSIONS:
if version[:2] in PYTHON_VERSIONS:
v_str = "opcode_%s" % version_tuple_to_str(
version, start=0, end=2, delimiter=""
)
if is_pypy:
v_str = "opcode_%spypy" % (int(version * 10))
else:
v_str = "opcode_%s" % (int(version * 10))
v_str += "pypy"
exec("from xdis.opcodes import %s" % v_str)
exec("self.opc = %s" % v_str)
else:
raise TypeError("%s is not a Python version I know about" % version)
raise TypeError(
"%s is not a Python version I know about"
% version_tuple_to_str(version)
)
self.opname = self.opc.opname
@@ -146,7 +159,7 @@ class Scanner(object):
# Offset: lineno pairs, only for offsets which start line.
# Locally we use list for more convenient iteration using indices
if self.version > 1.4:
if self.version > (1, 4):
linestarts = list(self.opc.findlinestarts(code_obj))
else:
linestarts = [[0, 1]]
@@ -535,8 +548,8 @@ def get_scanner(version, is_pypy=False, show_asm=None):
version = CANONIC2VERSION[canonic_version]
# Pick up appropriate scanner
if version in PYTHON_VERSIONS:
v_str = "%s" % (int(version * 10))
if version[:2] in PYTHON_VERSIONS:
v_str = version_tuple_to_str(version, start=0, end=2, delimiter="")
try:
import importlib
@@ -568,7 +581,10 @@ def get_scanner(version, is_pypy=False, show_asm=None):
"scan.Scanner%s(show_asm=show_asm)" % v_str, locals(), globals()
)
else:
raise RuntimeError("Unsupported Python version %s" % version)
raise RuntimeError(
"Unsupported Python version, %s, for decompilation"
% version_tuple_to_str(version)
)
return scanner
@@ -578,5 +594,5 @@ if __name__ == "__main__":
co = inspect.currentframe().f_code
# scanner = get_scanner('2.7.13', True)
# scanner = get_scanner(sys.version[:5], False)
scanner = get_scanner(uncompyle6.PYTHON_VERSION, IS_PYPY, True)
scanner = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY, True)
tokens, customize = scanner.ingest(co, {}, show_asm="after")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -86,7 +86,21 @@ class Token: # Python 2.4 can't have empty ()
self.pattr = None
if opc is None:
from xdis.std import _std_api
try:
from xdis.std import _std_api
except KeyError:
e = sys.exec_info()[1]
print("I don't know about Python version %s yet." % e)
try:
version_tuple = tuple(int(i) for i in str(e)[1:-1].split("."))
except:
pass
else:
if version_tuple > (3, 9):
print("Python versions 3.9 and greater are not supported.")
else:
print("xdis might need to be informed about version {e}" % e)
return
self.opc = _std_api.opc
else:

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2019-2020 by Rocky Bernstein
# Copyright (c) 2019-2021 by Rocky Bernstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -275,11 +275,16 @@ def customize_for_version37(self, version):
and opname == "CALL_FUNCTION_1"
or not re.match(r"\d", opname[-1])
):
if node[0][0] == "_mklambda":
template = "(%c)(%p)"
else:
template = "%c(%p)"
self.template_engine(
("%c(%p)",
(template,
(0, "expr"),
(1, PRECEDENCE["yield"]-1)),
node)
node
)
self.prec = p
self.prune()
else:

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