You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Compare commits
200 Commits
release-py
...
release-py
Author | SHA1 | Date | |
---|---|---|---|
|
0f34fb6726 | ||
|
9128813798 | ||
|
b7eae4f360 | ||
|
75d90b933c | ||
|
3aed87ac5e | ||
|
af873f1e88 | ||
|
ee72f6d685 | ||
|
8d6d8b31e0 | ||
|
85e5d72529 | ||
|
7209405b2c | ||
|
a8f89fa006 | ||
|
dc79ec3a25 | ||
|
252f18400c | ||
|
bb5bec29f7 | ||
|
ad92f53e39 | ||
|
5c0fd39e0b | ||
|
7a05a36f63 | ||
|
28e33f4b92 | ||
|
29e413c13c | ||
|
7c91694cf9 | ||
|
ac9c7d1047 | ||
|
3721722764 | ||
|
2db15210c9 | ||
|
58f9935bd6 | ||
|
404517e426 | ||
|
e4127b34a5 | ||
|
df6f39cb26 | ||
|
e77ccba40e | ||
|
2fcb7a62e1 | ||
|
afb79f84e2 | ||
|
1f462cf503 | ||
|
c0a86e6b9f | ||
|
909ec81b55 | ||
|
94e57f3ccf | ||
|
4cf0f83257 | ||
|
950dd05791 | ||
|
e9ff6136b5 | ||
|
f9f5a64c87 | ||
|
a4971ee27d | ||
|
82a64b421d | ||
|
c048b26d4e | ||
|
ece788e09e | ||
|
d8e212c9ea | ||
|
1e72250f79 | ||
|
ef92f08f56 | ||
|
bdc751f444 | ||
|
b0e139e6cc | ||
|
1e95ebd5f6 | ||
|
d1dc5a404c | ||
|
ae75b4f677 | ||
|
18f253ffbe | ||
|
6b01da76ea | ||
|
f55febfbf0 | ||
|
df1772164c | ||
|
47f0d5cd69 | ||
|
4bd6e609dd | ||
|
0897d47afa | ||
|
b7ad271aa2 | ||
|
060c8df174 | ||
|
dba73d6f02 | ||
|
be855a3001 | ||
|
0b8edba0dd | ||
|
5a2e5cf6bb | ||
|
655ab203ea | ||
|
793e9ced6a | ||
|
ee7fda2869 | ||
|
f2d141c466 | ||
|
cb7bbbb2e1 | ||
|
d7fdafc1f7 | ||
|
1cac7d50c1 | ||
|
4171dfc7e9 | ||
|
df7310e8ca | ||
|
8479e66add | ||
|
4281083641 | ||
|
5102e5f6e0 | ||
|
bee35aa05d | ||
|
4828ae99a3 | ||
|
26b60f6fb8 | ||
|
18133794e6 | ||
|
499acce8e6 | ||
|
3ea0d67be9 | ||
|
f41a16b7e9 | ||
|
6ba779b915 | ||
|
2b9887ce9b | ||
|
86ba02d5f2 | ||
|
d42fee1b50 | ||
|
54e9de4a7d | ||
|
f8798945ab | ||
|
c1a5d3ce8d | ||
|
a941326a30 | ||
|
5b36e45805 | ||
|
a774cc1892 | ||
|
e03274c78c | ||
|
5ff3a54ed7 | ||
|
1323500a76 | ||
|
9923a4c775 | ||
|
dd20a38412 | ||
|
b83bcb871a | ||
|
076a40c06d | ||
|
504845668c | ||
|
375101d960 | ||
|
2a393a408b | ||
|
e596fb0917 | ||
|
0ce23288cb | ||
|
1ecceb6471 | ||
|
7d1b306b10 | ||
|
7ce05a1934 | ||
|
291b8e0f90 | ||
|
68c646f1bb | ||
|
28bd433c9a | ||
|
e1f41b724e | ||
|
2fc80fc747 | ||
|
a173f27e7c | ||
|
e4e9cb2758 | ||
|
3b3ff705f9 | ||
|
a59e9c1aa8 | ||
|
8483a5102b | ||
|
d03a4235df | ||
|
7a4df3226e | ||
|
b512b20b56 | ||
|
50f6625cd1 | ||
|
4096d310e4 | ||
|
5c6c6c663d | ||
|
8f09437537 | ||
|
d89153f910 | ||
|
b8856993d2 | ||
|
4f6d3a3d7e | ||
|
e930c9c6ef | ||
|
3471d11dd5 | ||
|
2a0a6c904c | ||
|
2d6f31df97 | ||
|
d8d8ed60d7 | ||
|
0f525c142d | ||
|
ee4d166e71 | ||
|
7720c8aa10 | ||
|
003ad0ceef | ||
|
aff0cd4baa | ||
|
dd98eb8764 | ||
|
ee439540da | ||
|
9539a5c95c | ||
|
6899f2bd96 | ||
|
97f8d91e35 | ||
|
b0250f4f9a | ||
|
f89a3e8fa1 | ||
|
209f19c1da | ||
|
76f7bae0a6 | ||
|
a93bec73cf | ||
|
997942e235 | ||
|
7c4b82243b | ||
|
92c0534cd4 | ||
|
256d19d9b4 | ||
|
56f10a8cfa | ||
|
82d10e025c | ||
|
2ac85acca5 | ||
|
b96e1df14b | ||
|
90930b66ce | ||
|
164168e7f4 | ||
|
040ed20b59 | ||
|
f06bd69858 | ||
|
ef03e7151d | ||
|
5a7755e047 | ||
|
3aadd0574e | ||
|
eff663cc4e | ||
|
9caceed001 | ||
|
a11b290a81 | ||
|
bba9c577d1 | ||
|
c4baec28de | ||
|
62da9f4583 | ||
|
890230b791 | ||
|
f72070e6d0 | ||
|
94832d654f | ||
|
77742532aa | ||
|
e233b2f63a | ||
|
0742f0b83f | ||
|
f36acf6faa | ||
|
96617c0895 | ||
|
e50cd1e07d | ||
|
c8c6f1a63d | ||
|
850500c7ad | ||
|
08ed185608 | ||
|
39d79217ca | ||
|
a2e34ab75c | ||
|
5c2af69925 | ||
|
1b4b6b334e | ||
|
482dbb5c82 | ||
|
55ffaa1aff | ||
|
79d5790e3f | ||
|
64b75625a9 | ||
|
5ddbea73f4 | ||
|
fad5089175 | ||
|
52262dc38a | ||
|
b61255535e | ||
|
ce58ed7434 | ||
|
01859ce820 | ||
|
ada786e08c | ||
|
cfb5c442e2 | ||
|
37f953c353 | ||
|
4d84a723f4 | ||
|
ddbfc168c5 | ||
|
a463220df2 |
@@ -1,80 +0,0 @@
|
||||
version: 2
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/rocky/python-uncompyle6
|
||||
parallelism: 1
|
||||
shell: /bin/bash --login
|
||||
# CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did.
|
||||
# If any of these refer to each other, rewrite them so that they don't or see https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables .
|
||||
environment:
|
||||
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
|
||||
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
|
||||
COMPILE: --compile
|
||||
# To see the list of pre-built images that CircleCI provides for most common languages see
|
||||
# https://circleci.com/docs/2.0/circleci-images/
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
steps:
|
||||
# Machine Setup
|
||||
# If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each
|
||||
# The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out.
|
||||
- checkout
|
||||
# Prepare for artifact and test results collection equivalent to how it was done on 1.0.
|
||||
# In many cases you can simplify this from what is generated here.
|
||||
# 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/'
|
||||
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
|
||||
# This is based on your 1.0 configuration file or project settings
|
||||
- run:
|
||||
working_directory: ~/rocky/python-uncompyle6
|
||||
command: pip install --user virtualenv && pip install --user nose && pip install
|
||||
--user pep8
|
||||
# Dependencies
|
||||
# This would typically go in either a build or a build-and-test job when using workflows
|
||||
# Restore the dependency cache
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v2-dependencies-{{ .Branch }}-
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v2-dependencies-
|
||||
|
||||
- run:
|
||||
command: | # Use pip to install dependengcies
|
||||
sudo pip install --user --upgrade setuptools
|
||||
# Until the next release
|
||||
sudo pip install git+https://github.com/rocky/python-xdis#egg=xdis
|
||||
pip install --user -e .
|
||||
pip install --user -r requirements-dev.txt
|
||||
|
||||
# Save dependency cache
|
||||
- save_cache:
|
||||
key: v2-dependencies-{{ .Branch }}-{{ epoch }}
|
||||
paths:
|
||||
# This is a broad list of cache paths to include many possible development environments
|
||||
# You can probably delete some of these entries
|
||||
- vendor/bundle
|
||||
- ~/virtualenvs
|
||||
- ~/.m2
|
||||
- ~/.ivy2
|
||||
- ~/.bundle
|
||||
- ~/.cache/bower
|
||||
|
||||
# Test
|
||||
# This would typically be a build job when using workflows, possibly combined with build
|
||||
# This is based on your 1.0 configuration file or project settings
|
||||
- run: sudo python ./setup.py develop && make check-3.6
|
||||
- run: cd ./test/stdlib && bash ./runtests.sh 'test_[p-z]*.py'
|
||||
# Teardown
|
||||
# If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each
|
||||
# Save test results
|
||||
- store_test_results:
|
||||
path: /tmp/circleci-test-results
|
||||
# Save artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-test-results
|
||||
# The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass
|
||||
resource_class: large
|
31
.github/workflows/osx.yml
vendored
31
.github/workflows/osx.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: uncompyle6 (osx)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS]
|
||||
python-version: [3.7, 3.8]
|
||||
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+https://github.com/rocky/python-xdis#egg=xdis
|
||||
pip install -e .
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Test uncompyle6
|
||||
run: |
|
||||
make check
|
30
.github/workflows/ubuntu.yml
vendored
30
.github/workflows/ubuntu.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: uncompyle6 (ubuntu)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8]
|
||||
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+https://github.com/rocky/python-xdis#egg=xdis
|
||||
pip install -e .
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Test uncompyle6
|
||||
run: |
|
||||
make check
|
31
.github/workflows/windows.yml
vendored
31
.github/workflows/windows.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: uncompyle6 (windows)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows]
|
||||
python-version: [3.7, 3.8]
|
||||
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+https://github.com/rocky/python-xdis#egg=xdis
|
||||
pip install -e .
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Test uncompyle6
|
||||
run: |
|
||||
make check
|
@@ -9,14 +9,3 @@ repos:
|
||||
stages: [commit]
|
||||
- id: end-of-file-fixer
|
||||
stages: [commit]
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
stages: [commit]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.12.1
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
stages: [commit]
|
||||
|
20
NEWS.md
20
NEWS.md
@@ -1,3 +1,23 @@
|
||||
3.9.1: 2024-05-15
|
||||
=================
|
||||
|
||||
Lots of changes major changes. track xdis API has changes.
|
||||
|
||||
Separate Phases more clearly:
|
||||
* disassembly
|
||||
* tokenization
|
||||
* parsing
|
||||
* abstracting to AST (more is done in newer projects)
|
||||
* printing
|
||||
|
||||
Although we do not decompile bytecode greater than 3.8, code supports running from up to 3.12.
|
||||
|
||||
Many bugs fixed.
|
||||
|
||||
A lot of Linting and coding style modernization.
|
||||
|
||||
Work done in preparation for Blackhat Asia 2024
|
||||
|
||||
3.9.0: 2022-12-22
|
||||
=================
|
||||
|
||||
|
@@ -62,6 +62,8 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Software Development :: Debuggers",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
|
0
admin-tools/check-3.3-3.5-versions.sh
Normal file → Executable file
0
admin-tools/check-3.3-3.5-versions.sh
Normal file → Executable file
@@ -3,9 +3,9 @@ PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
cd $make_dist_uncompyle6_owd
|
||||
}
|
||||
owd=$(pwd)
|
||||
make_dist_uncompyle6_owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
@@ -21,6 +21,11 @@ source $PACKAGE/version.py
|
||||
echo $__version__
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
echo --- $pyversion ---
|
||||
if [[ ${pyversion:0:4} == "pypy" ]] ; then
|
||||
echo "$pyversion - PyPy does not get special packaging"
|
||||
continue
|
||||
fi
|
||||
if ! pyenv local $pyversion ; then
|
||||
exit $?
|
||||
fi
|
||||
@@ -41,3 +46,4 @@ tarball=dist/${PACKAGE}-${__version_}_-tar.gz
|
||||
if [[ -f $tarball ]]; then
|
||||
rm -v dist/${PACKAGE}-${__version__}-tar.gz
|
||||
fi
|
||||
finish
|
||||
|
49
admin-tools/make-dist-3.0-3.2.sh
Executable file
49
admin-tools/make-dist-3.0-3.2.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $uncompyle6_30_make_dist_owd
|
||||
}
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
uncompyle6_30_make_dist_owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
if ! source ./pyenv-3.0-3.2-versions ; then
|
||||
exit $?
|
||||
fi
|
||||
if ! source ./setup-python-3.0.sh ; then
|
||||
exit $?
|
||||
fi
|
||||
|
||||
cd ..
|
||||
source $PACKAGE/version.py
|
||||
echo $__version__
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
echo --- $pyversion ---
|
||||
if [[ ${pyversion:0:4} == "pypy" ]] ; then
|
||||
echo "$pyversion - PyPy does not get special packaging"
|
||||
continue
|
||||
fi
|
||||
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
|
||||
echo === $pyversion ===
|
||||
done
|
||||
|
||||
python ./setup.py sdist
|
||||
tarball=dist/${PACKAGE}-${__version__}.tar.gz
|
||||
if [[ -f $tarball ]]; then
|
||||
mv -v $tarball dist/${PACKAGE}_31-${__version__}.tar.gz
|
||||
fi
|
||||
finish
|
@@ -3,11 +3,11 @@ PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
cd $uncompyle6_33_make_owd
|
||||
}
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
owd=$(pwd)
|
||||
uncompyle6_33_make_owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
if ! source ./pyenv-3.3-3.5-versions ; then
|
||||
@@ -22,6 +22,11 @@ source $PACKAGE/version.py
|
||||
echo $__version__
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
echo --- $pyversion ---
|
||||
if [[ ${pyversion:0:4} == "pypy" ]] ; then
|
||||
echo "$pyversion - PyPy does not get special packaging"
|
||||
continue
|
||||
fi
|
||||
if ! pyenv local $pyversion ; then
|
||||
exit $?
|
||||
fi
|
||||
@@ -33,6 +38,12 @@ for pyversion in $PYVERSIONS; do
|
||||
rm -fr build
|
||||
python setup.py bdist_egg bdist_wheel
|
||||
mv -v dist/${PACKAGE}-$__version__-{py2.py3,py$first_two}-none-any.whl
|
||||
echo === $pyversion ===
|
||||
done
|
||||
|
||||
python ./setup.py sdist
|
||||
tarball=dist/${PACKAGE}-${__version__}.tar.gz
|
||||
if [[ -f $tarball ]]; then
|
||||
mv -v $tarball dist/${PACKAGE}_31-${__version__}.tar.gz
|
||||
fi
|
||||
finish
|
||||
|
@@ -3,11 +3,11 @@ PACKAGE=uncompyle6
|
||||
|
||||
# FIXME put some of the below in a common routine
|
||||
function finish {
|
||||
cd $owd
|
||||
cd $make_uncompyle6_newest_owd
|
||||
}
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
owd=$(pwd)
|
||||
make_uncompyle6_newest_owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
if ! source ./pyenv-newest-versions ; then
|
||||
@@ -22,6 +22,11 @@ source $PACKAGE/version.py
|
||||
echo $__version__
|
||||
|
||||
for pyversion in $PYVERSIONS; do
|
||||
echo --- $pyversion ---
|
||||
if [[ ${pyversion:0:4} == "pypy" ]] ; then
|
||||
echo "$pyversion - PyPy does not get special packaging"
|
||||
continue
|
||||
fi
|
||||
if ! pyenv local $pyversion ; then
|
||||
exit $?
|
||||
fi
|
||||
@@ -36,3 +41,4 @@ for pyversion in $PYVERSIONS; do
|
||||
done
|
||||
|
||||
python ./setup.py sdist
|
||||
finish
|
||||
|
@@ -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.10 3.3.7 3.4.10'
|
||||
export PYVERSIONS=' 3.3.7 3.4.10 3.5.10 '
|
||||
|
64
pyproject.toml
Normal file
64
pyproject.toml
Normal file
@@ -0,0 +1,64 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=61.2",
|
||||
]
|
||||
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
authors = [
|
||||
{name = "Rocky Bernstein", email = "rb@dustyfeet.com"},
|
||||
]
|
||||
|
||||
name = "uncompyle6"
|
||||
description = "Python cross-version byte-code library and disassembler"
|
||||
dependencies = [
|
||||
"click",
|
||||
"spark-parser >= 1.8.9, < 1.9.0",
|
||||
"xdis >= 6.0.8, < 6.2.0",
|
||||
]
|
||||
readme = "README.rst"
|
||||
license = {text = "GPL"}
|
||||
keywords = ["Python bytecode", "bytecode", "disassembler"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Programming Language :: Python :: 2.4",
|
||||
"Programming Language :: Python :: 2.5",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3.0",
|
||||
"Programming Language :: Python :: 3.1",
|
||||
"Programming Language :: Python :: 3.2",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"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 :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/rocky/python-uncompyle6"
|
||||
Downloads = "https://github.com/rocky/python-uncompyle6/releases"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pre-commit",
|
||||
"pytest",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
uncompyle6 = "uncompyle6.bin.uncompile:main_bin"
|
||||
uncompyle6-tokenize = "uncompyle6.bin.pydisassemble:main"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "uncompyle6.version.__version__"}
|
@@ -123,6 +123,7 @@ def test_grammar():
|
||||
opcode_set.add("THEN")
|
||||
check_tokens(tokens, opcode_set)
|
||||
elif PYTHON_VERSION_TRIPLE[:2] == (3, 4):
|
||||
ignore_set.add("LOAD_ARG") # Used in grammar for comprehension. But not in 3.4
|
||||
ignore_set.add("LOAD_CLASSNAME")
|
||||
ignore_set.add("STORE_LOCALS")
|
||||
opcode_set = set(s.opc.opname).union(ignore_set)
|
||||
|
13
setup.py
13
setup.py
@@ -5,16 +5,21 @@ import sys
|
||||
"""Setup script for the 'uncompyle6' distribution."""
|
||||
|
||||
SYS_VERSION = sys.version_info[0:2]
|
||||
if not ((2, 4) <= SYS_VERSION < (3, 13)):
|
||||
mess = "Python Release 2.6 .. 3.12 are supported in this code branch."
|
||||
if not ((3, 3) <= SYS_VERSION < (3, 6)):
|
||||
mess = "Python Release 3.3 .. 3.5 are supported in this code branch."
|
||||
if (2, 4) <= SYS_VERSION <= (2, 7):
|
||||
mess += (
|
||||
"\nFor your Python, version %s, use the python-2.4 code/branch."
|
||||
% sys.version[0:3]
|
||||
)
|
||||
if (3, 3) <= SYS_VERSION < (3, 6):
|
||||
if SYS_VERSION >= (3, 6):
|
||||
mess += (
|
||||
"\nFor your Python, version %s, use the python-3.3-to-3.5 code/branch."
|
||||
"\nFor your Python, version %s, use the master code/branch."
|
||||
% sys.version[0:3]
|
||||
)
|
||||
if (3, 0) >= SYS_VERSION < (3, 3):
|
||||
mess += (
|
||||
"\nFor your Python, version %s, use the python-3.0-to-3.2 code/branch."
|
||||
% sys.version[0:3]
|
||||
)
|
||||
elif SYS_VERSION < (2, 4):
|
||||
|
@@ -64,8 +64,9 @@ PATTERNS = ("*.pyc", "*.pyo")
|
||||
|
||||
def main():
|
||||
usage_short = (
|
||||
f"""usage: {program} FILE...
|
||||
"""usage: %s FILE...
|
||||
Type -h for for full help."""
|
||||
% program
|
||||
)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
@@ -78,7 +79,7 @@ Type -h for for full help."""
|
||||
sys.argv[1:], "hVU", ["help", "version", "uncompyle6"]
|
||||
)
|
||||
except getopt.GetoptError as e:
|
||||
print(f"{os.path.basename(sys.argv[0])}: {e}", file=sys.stderr)
|
||||
print("%s: %s" % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
for opt, val in opts:
|
||||
@@ -86,7 +87,7 @@ Type -h for for full help."""
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
elif opt in ("-V", "--version"):
|
||||
print(f"{program} {__version__}")
|
||||
print("%s %s" % (program, __version__))
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(opt)
|
||||
@@ -97,7 +98,7 @@ Type -h for for full help."""
|
||||
if os.path.exists(files[0]):
|
||||
disassemble_file(file, sys.stdout)
|
||||
else:
|
||||
print(f"Can't read {files[0]} - skipping", file=sys.stderr)
|
||||
print("Can't read %s - skipping" % files[0], file=sys.stderr)
|
||||
pass
|
||||
pass
|
||||
return
|
||||
|
@@ -5,12 +5,10 @@
|
||||
# by Rocky Bernstein
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
#
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
import click
|
||||
from xdis.version_info import version_tuple_to_str
|
||||
@@ -159,19 +157,27 @@ def main_bin(
|
||||
"""
|
||||
|
||||
version_tuple = sys.version_info[0:2]
|
||||
if version_tuple < (3, 6):
|
||||
print(
|
||||
f"Error: This version of the {program} runs from Python 3.6 or greater."
|
||||
f"You need another branch of this code for Python before 3.6."
|
||||
f""" \n\tYou have version: {version_tuple_to_str()}."""
|
||||
)
|
||||
sys.exit(-1)
|
||||
if not ((3, 3) <= version_tuple < (3, 6)):
|
||||
if version_tuple > (3, 5):
|
||||
print(
|
||||
"This version of the {program} is tailored for Python 3.3 to 3.5.\n"
|
||||
"It may run on other versions, but there are problems, switch to code "
|
||||
"from another branch.\n"
|
||||
"You have version: %s." % version_tuple_to_str()
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Error: This version of the {program} runs from Python 3.3 to 3.5.\n"
|
||||
"You need another branch of this code for other Python versions."
|
||||
" \n\tYou have version: %s." % version_tuple_to_str()
|
||||
)
|
||||
sys.exit(-1)
|
||||
|
||||
numproc = 0
|
||||
out_base = None
|
||||
|
||||
out_base = None
|
||||
source_paths: List[str] = []
|
||||
source_paths = []
|
||||
timestamp = False
|
||||
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
||||
pyc_paths = files
|
||||
|
@@ -21,7 +21,6 @@ import py_compile
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Any, Optional, TextIO, Tuple
|
||||
|
||||
from xdis import iscode
|
||||
from xdis.load import load_module
|
||||
@@ -38,7 +37,7 @@ from uncompyle6.version import __version__
|
||||
# from uncompyle6.linenumbers import line_number_mapping
|
||||
|
||||
|
||||
def _get_outstream(outfile: str) -> Any:
|
||||
def _get_outstream(outfile):
|
||||
"""
|
||||
Return an opened output file descriptor for ``outfile``.
|
||||
"""
|
||||
@@ -66,9 +65,9 @@ def syntax_check(filename: str) -> bool:
|
||||
|
||||
def decompile(
|
||||
co,
|
||||
bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE,
|
||||
out: Optional[TextIO] = sys.stdout,
|
||||
showasm: Optional[str] = None,
|
||||
bytecode_version=PYTHON_VERSION_TRIPLE,
|
||||
out=sys.stdout,
|
||||
showasm=None,
|
||||
showast={},
|
||||
timestamp=None,
|
||||
showgrammar=False,
|
||||
@@ -82,7 +81,7 @@ def decompile(
|
||||
compile_mode="exec",
|
||||
start_offset: int = 0,
|
||||
stop_offset: int = -1,
|
||||
) -> Any:
|
||||
):
|
||||
"""
|
||||
ingests and deparses a given code block 'co'
|
||||
|
||||
@@ -101,13 +100,13 @@ def decompile(
|
||||
s += "\n"
|
||||
real_out.write(s)
|
||||
|
||||
assert iscode(co), f"""{co} does not smell like code"""
|
||||
assert iscode(co), "%s does not smell like code" % co
|
||||
|
||||
co_pypy_str = "PyPy " if is_pypy else ""
|
||||
run_pypy_str = "PyPy " if IS_PYPY else ""
|
||||
sys_version_lines = sys.version.split("\n")
|
||||
if source_encoding:
|
||||
write(f"# -*- coding: {source_encoding} -*-")
|
||||
write("# -*- coding: %s -*-" % source_encoding)
|
||||
write(
|
||||
"# uncompyle6 version %s\n"
|
||||
"# %sPython bytecode version base %s%s\n# Decompiled from: %sPython %s"
|
||||
@@ -121,9 +120,9 @@ def decompile(
|
||||
)
|
||||
)
|
||||
if co.co_filename:
|
||||
write(f"# Embedded file name: {co.co_filename}")
|
||||
write("# Embedded file name: %s" % co.co_filename)
|
||||
if timestamp:
|
||||
write(f"# Compiled at: {datetime.datetime.fromtimestamp(timestamp)}")
|
||||
write("# Compiled at: %s" % datetime.datetime.fromtimestamp(timestamp))
|
||||
if source_size:
|
||||
write("# Size of source mod 2**32: %d bytes" % source_size)
|
||||
|
||||
@@ -154,7 +153,7 @@ def decompile(
|
||||
(line_no, deparsed.source_linemap[line_no] + header_count)
|
||||
for line_no in sorted(deparsed.source_linemap.keys())
|
||||
]
|
||||
mapstream.write(f"\n\n# {linemap}\n")
|
||||
mapstream.write("\n\n# %s\n" % linemap)
|
||||
else:
|
||||
if do_fragments:
|
||||
deparse_fn = code_deparse_fragments
|
||||
@@ -178,26 +177,26 @@ def decompile(
|
||||
raise pysource.SourceWalkerError(str(e))
|
||||
|
||||
|
||||
def compile_file(source_path: str) -> str:
|
||||
def compile_file(source_path):
|
||||
if source_path.endswith(".py"):
|
||||
basename = source_path[:-3]
|
||||
else:
|
||||
basename = source_path
|
||||
|
||||
if hasattr(sys, "pypy_version_info"):
|
||||
bytecode_path = f"{basename}-pypy{version_tuple_to_str()}.pyc"
|
||||
bytecode_path = "%s-pypy%s.pyc" % (basename, version_tuple_to_str())
|
||||
else:
|
||||
bytecode_path = f"{basename}-{version_tuple_to_str()}.pyc"
|
||||
bytecode_path = "%s-%s.pyc" % (basename, version_tuple_to_str())
|
||||
|
||||
print(f"compiling {source_path} to {bytecode_path}")
|
||||
print("compiling %s to %s" % (source_path, bytecode_path))
|
||||
py_compile.compile(source_path, bytecode_path, "exec")
|
||||
return bytecode_path
|
||||
|
||||
|
||||
def decompile_file(
|
||||
filename: str,
|
||||
outstream: Optional[TextIO] = None,
|
||||
showasm: Optional[str] = None,
|
||||
outstream=None,
|
||||
showasm=None,
|
||||
showast={},
|
||||
showgrammar=False,
|
||||
source_encoding=None,
|
||||
@@ -205,7 +204,7 @@ def decompile_file(
|
||||
do_fragments=False,
|
||||
start_offset=0,
|
||||
stop_offset=-1,
|
||||
) -> Any:
|
||||
):
|
||||
"""
|
||||
decompile Python byte-code file (.pyc). Return objects to
|
||||
all of the deparsed objects found in `filename`.
|
||||
@@ -266,20 +265,20 @@ def decompile_file(
|
||||
# FIXME: combine into an options parameter
|
||||
def main(
|
||||
in_base: str,
|
||||
out_base: Optional[str],
|
||||
out_base,
|
||||
compiled_files: list,
|
||||
source_files: list,
|
||||
outfile: Optional[str] = None,
|
||||
showasm: Optional[str] = None,
|
||||
outfile=None,
|
||||
showasm=None,
|
||||
showast={},
|
||||
do_verify: Optional[str] = None,
|
||||
do_verify=None,
|
||||
showgrammar: bool = False,
|
||||
source_encoding=None,
|
||||
do_linemaps=False,
|
||||
do_fragments=False,
|
||||
start_offset: int = 0,
|
||||
stop_offset: int = -1,
|
||||
) -> Tuple[int, int, int, int]:
|
||||
):
|
||||
"""
|
||||
in_base base directory for input files
|
||||
out_base base directory for output files (ignored when
|
||||
@@ -303,7 +302,7 @@ def main(
|
||||
infile = osp.join(in_base, filename)
|
||||
# print("XXX", infile)
|
||||
if not osp.exists(infile):
|
||||
sys.stderr.write(f"File '{infile}' doesn't exist. Skipped\n")
|
||||
sys.stderr.write("File '%s' doesn't exist. Skipped\n" % infile)
|
||||
continue
|
||||
|
||||
if do_linemaps:
|
||||
@@ -358,11 +357,11 @@ def main(
|
||||
):
|
||||
if e[0] != last_mod:
|
||||
line = "=" * len(e[0])
|
||||
outstream.write(f"{line}\n{e[0]}\n{line}\n")
|
||||
outstream.write("%s\n%s\n%s\n" % (line, e[0], line))
|
||||
last_mod = e[0]
|
||||
info = offsets[e]
|
||||
extract_info = deparsed_object.extract_node_info(info)
|
||||
outstream.write(f"{info.node.format().strip()}" + "\n")
|
||||
outstream.write("%s" % info.node.format().strip() + "\n")
|
||||
outstream.write(extract_info.selectedLine + "\n")
|
||||
outstream.write(extract_info.markerLine + "\n\n")
|
||||
pass
|
||||
@@ -372,49 +371,41 @@ def main(
|
||||
deparsed_object.f.close()
|
||||
if PYTHON_VERSION_TRIPLE[:2] != deparsed_object.version[:2]:
|
||||
sys.stdout.write(
|
||||
f"\n# skipping running {deparsed_object.f.name}; it is "
|
||||
f"{version_tuple_to_str(deparsed_object.version, end=2)}, "
|
||||
"and we are "
|
||||
f"{version_tuple_to_str(PYTHON_VERSION_TRIPLE, end=2)}\n"
|
||||
"\n# skipping running %s; it is %s and we are %s"
|
||||
% (
|
||||
deparsed_object.f.name,
|
||||
version_tuple_to_str(deparsed_object.version, end=2),
|
||||
version_tuple_to_str(PYTHON_VERSION_TRIPLE, end=2),
|
||||
)
|
||||
)
|
||||
else:
|
||||
check_type = "syntax check"
|
||||
if do_verify == "run":
|
||||
check_type = "run"
|
||||
if PYTHON_VERSION_TRIPLE >= (3, 7):
|
||||
result = subprocess.run(
|
||||
[sys.executable, deparsed_object.f.name],
|
||||
capture_output=True,
|
||||
)
|
||||
valid = result.returncode == 0
|
||||
output = result.stdout.decode()
|
||||
if output:
|
||||
print(output)
|
||||
pass
|
||||
else:
|
||||
result = subprocess.run(
|
||||
[sys.executable, deparsed_object.f.name],
|
||||
)
|
||||
valid = result.returncode == 0
|
||||
pass
|
||||
return_code = subprocess.call(
|
||||
[sys.executable, deparsed_object.f.name],
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
valid = return_code == 0
|
||||
if not valid:
|
||||
print(result.stderr.decode())
|
||||
|
||||
sys.stderr.write("Got return code %d\n" % return_code)
|
||||
else:
|
||||
valid = syntax_check(deparsed_object.f.name)
|
||||
|
||||
if not valid:
|
||||
verify_failed_files += 1
|
||||
sys.stderr.write(
|
||||
f"\n# {check_type} failed on file {deparsed_object.f.name}\n"
|
||||
"\n# %s failed on file %s\n"
|
||||
% (check_type, deparsed_object.f.name)
|
||||
)
|
||||
|
||||
# sys.stderr.write(f"Ran {deparsed_object.f.name}\n")
|
||||
# sys.stderr.write("Ran %\n" % deparsed_object.f.name)
|
||||
pass
|
||||
tot_files += 1
|
||||
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write(f"\n# file {infile}\n# {e}\n")
|
||||
sys.stderr.write("\n# file %s\n# %s\n" % (infile, e))
|
||||
failed_files += 1
|
||||
tot_files += 1
|
||||
except KeyboardInterrupt:
|
||||
@@ -422,19 +413,21 @@ def main(
|
||||
outstream.close()
|
||||
os.remove(outfile)
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write(f"\nLast file: {infile} ")
|
||||
sys.stderr.write("\nLast file: %s " % (infile))
|
||||
raise
|
||||
except RuntimeError as e:
|
||||
sys.stdout.write(f"\n{str(e)}\n")
|
||||
sys.stdout.write("\n%s\n" % str(e))
|
||||
if str(e).startswith("Unsupported Python"):
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write(f"\n# Unsupported bytecode in file {infile}\n# {e}\n")
|
||||
sys.stderr.write(
|
||||
"\n# Unsupported bytecode in file %s\n# %s\n" % (infile, e)
|
||||
)
|
||||
else:
|
||||
if outfile:
|
||||
outstream.close()
|
||||
os.remove(outfile)
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write(f"\nLast file: {infile} ")
|
||||
sys.stderr.write("\nLast file: %s " % (infile))
|
||||
raise
|
||||
|
||||
# except:
|
||||
@@ -511,5 +504,9 @@ def status_msg(tot_files, okay_files, failed_files, verify_failed_files):
|
||||
return "\n# Successfully decompiled file"
|
||||
pass
|
||||
pass
|
||||
mess = f"decompiled {tot_files} files: {okay_files} okay, {failed_files} failed"
|
||||
mess = "decompiled %i files: %i okay, %i failed" % (
|
||||
tot_files,
|
||||
okay_files,
|
||||
failed_files,
|
||||
)
|
||||
return mess
|
||||
|
@@ -64,7 +64,7 @@ class Python14Parser(Python15Parser):
|
||||
|
||||
if opname_base == "UNPACK_VARARG":
|
||||
if token.attr > 1:
|
||||
self.addRule(f"star_args ::= RESERVE_FAST {opname} args_store", nop_func)
|
||||
self.addRule("star_args ::= RESERVE_FAST %s args_store" % opname, nop_func)
|
||||
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
|
@@ -1240,7 +1240,7 @@ class Python3Parser(PythonParser):
|
||||
) % (
|
||||
pos_kw_tuple[0],
|
||||
pos_kw_tuple[1],
|
||||
"annotate_pair " * (annotate_args),
|
||||
"annotate_tuple " * (annotate_args),
|
||||
opname,
|
||||
)
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
|
@@ -320,18 +320,24 @@ class Python37BaseParser(PythonParser):
|
||||
|
||||
elif opname in ("BUILD_CONST_LIST", "BUILD_CONST_DICT", "BUILD_CONST_SET"):
|
||||
if opname == "BUILD_CONST_DICT":
|
||||
rule = f"""
|
||||
rule = (
|
||||
"""
|
||||
add_consts ::= ADD_VALUE*
|
||||
const_list ::= COLLECTION_START add_consts {opname}
|
||||
const_list ::= COLLECTION_START add_consts %s
|
||||
dict ::= const_list
|
||||
expr ::= dict
|
||||
"""
|
||||
% opname
|
||||
)
|
||||
else:
|
||||
rule = f"""
|
||||
rule = (
|
||||
"""
|
||||
add_consts ::= ADD_VALUE*
|
||||
const_list ::= COLLECTION_START add_consts {opname}
|
||||
const_list ::= COLLECTION_START add_consts %s
|
||||
expr ::= const_list
|
||||
"""
|
||||
% opname
|
||||
)
|
||||
self.addRule(rule, nop_func)
|
||||
|
||||
elif opname_base == "BUILD_CONST_KEY_MAP":
|
||||
@@ -1263,9 +1269,14 @@ class Python37BaseParser(PythonParser):
|
||||
import traceback
|
||||
|
||||
print(
|
||||
f"Exception in {fn.__name__} {sys.exc_info()[1]}\n"
|
||||
+ f"rule: {rule2str(rule)}\n"
|
||||
+ f"offsets {tokens[first].offset} .. {tokens[last].offset}"
|
||||
("Exception in %s %s\n" + "rule: %s\n" + "offsets %s .. %s")
|
||||
% (
|
||||
fn.__name__,
|
||||
sys.exc_info()[1],
|
||||
rule2str(rule),
|
||||
tokens[first].offset,
|
||||
tokens[last].offset,
|
||||
)
|
||||
)
|
||||
print(traceback.print_tb(sys.exc_info()[2], -1))
|
||||
raise ParserError(tokens[last], tokens[last].off2int(), self.debug["rules"])
|
||||
|
@@ -5,9 +5,7 @@
|
||||
|
||||
|
||||
|
||||
def and_invalid(
|
||||
self, lhs: str, n: int, rule, ast, tokens: list, first: int, last: int
|
||||
) -> bool:
|
||||
def and_invalid( self, lhs, n, rule, ast, tokens, first, last):
|
||||
jmp = ast[1]
|
||||
if jmp.kind.startswith("jmp_"):
|
||||
if last == n:
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
def and_not_check(
|
||||
self, lhs, n, rule, ast, tokens, first, last
|
||||
) -> bool:
|
||||
):
|
||||
jmp = ast[1]
|
||||
if jmp.kind.startswith("jmp_"):
|
||||
if last == n:
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# Copyright (c) 2022 Rocky Bernstein
|
||||
# Copyright (c) 2022, 2024 Rocky Bernstein
|
||||
|
||||
from uncompyle6.scanners.tok import Token
|
||||
|
||||
|
||||
def for_block_invalid(self, lhs, n, rule, tree, tokens, first: int, last: int) -> bool:
|
||||
def for_block_invalid(self, lhs, n, rule, tree, tokens, first, last):
|
||||
# print("XXX", first, last)
|
||||
# for t in range(first, last):
|
||||
# print(tokens[t])
|
||||
|
@@ -13,9 +13,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
def joined_str_invalid(
|
||||
self, lhs: str, n: int, rule, tree, tokens: list, first: int, last: int
|
||||
) -> bool:
|
||||
def joined_str_invalid(self, lhs, n, rule, tree, tokens, first, last):
|
||||
# In Python 3.8, there is a new "=" specifier.
|
||||
# See https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging
|
||||
# We detect this here inside joined_str by looking for an
|
||||
|
@@ -1,9 +1,6 @@
|
||||
import sys
|
||||
from uncompyle6.scanners.tok import NoneToken
|
||||
from spark_parser.ast import AST as spark_AST
|
||||
|
||||
intern = sys.intern
|
||||
|
||||
|
||||
class SyntaxTree(spark_AST):
|
||||
def __init__(self, *args, transformed_by=None, **kwargs):
|
||||
|
@@ -21,11 +21,9 @@ scanner/ingestion module. From here we call various version-specific
|
||||
scanners, e.g. for Python 2.7 or 3.4.
|
||||
"""
|
||||
|
||||
from abc import ABC
|
||||
import sys
|
||||
from array import array
|
||||
from collections import namedtuple
|
||||
from types import ModuleType
|
||||
from typing import Optional, Union
|
||||
|
||||
import xdis
|
||||
from xdis import (
|
||||
@@ -80,6 +78,7 @@ CANONIC2VERSION["3.5.2"] = 3.5
|
||||
|
||||
|
||||
# FIXME: DRY
|
||||
intern = sys.intern
|
||||
L65536 = 65536
|
||||
|
||||
|
||||
@@ -109,20 +108,19 @@ class Code:
|
||||
self._tokens, self._customize = scanner.ingest(co, classname, show_asm=show_asm)
|
||||
|
||||
|
||||
class Scanner(ABC):
|
||||
class Scanner:
|
||||
def __init__(self, version: tuple, show_asm=None, is_pypy=False):
|
||||
self.version = version
|
||||
self.show_asm = show_asm
|
||||
self.is_pypy = is_pypy
|
||||
|
||||
# Temoorary initialization.
|
||||
self.opc = ModuleType("uninitialized")
|
||||
|
||||
if version[:2] in PYTHON_VERSIONS:
|
||||
v_str = f"""opcode_{version_tuple_to_str(version, start=0, end=2, delimiter="")}"""
|
||||
v_str = "opcode_%s" % version_tuple_to_str(
|
||||
version, start=0, end=2, delimiter=""
|
||||
)
|
||||
if is_pypy:
|
||||
v_str += "pypy"
|
||||
exec(f"""from xdis.opcodes import {v_str}""")
|
||||
exec("from xdis.opcodes import %s" % v_str)
|
||||
exec("self.opc = %s" % v_str)
|
||||
else:
|
||||
raise TypeError(
|
||||
@@ -281,7 +279,7 @@ class Scanner(ABC):
|
||||
for _ in range(instruction_size(op, self.opc)):
|
||||
self.prev_op.append(offset)
|
||||
|
||||
def is_jump_forward(self, offset: int) -> bool:
|
||||
def is_jump_forward(self, offset):
|
||||
"""
|
||||
Return True if the code at offset is some sort of jump forward.
|
||||
That is, it is ether "JUMP_FORWARD" or an absolute jump that
|
||||
@@ -303,7 +301,7 @@ class Scanner(ABC):
|
||||
def prev_offset(self, offset: int) -> int:
|
||||
return self.insts[self.offset2inst_index[offset] - 1].offset
|
||||
|
||||
def get_inst(self, offset: int):
|
||||
def get_inst(self, offset):
|
||||
# Instructions can get moved as a result of EXTENDED_ARGS removal.
|
||||
# So if "offset" is not in self.offset2inst_index, then
|
||||
# we assume that it was an instruction moved back.
|
||||
@@ -314,7 +312,7 @@ class Scanner(ABC):
|
||||
assert self.code[offset] == self.opc.EXTENDED_ARG
|
||||
return self.insts[self.offset2inst_index[offset]]
|
||||
|
||||
def get_target(self, offset: int, extended_arg: int = 0) -> int:
|
||||
def get_target(self, offset, extended_arg=0):
|
||||
"""
|
||||
Get next instruction offset for op located at given <offset>.
|
||||
NOTE: extended_arg is no longer used
|
||||
@@ -327,14 +325,23 @@ class Scanner(ABC):
|
||||
target = next_offset(inst.opcode, self.opc, inst.offset)
|
||||
return target
|
||||
|
||||
def get_argument(self, pos: int):
|
||||
def get_argument(self, pos):
|
||||
arg = self.code[pos + 1] + self.code[pos + 2] * 256
|
||||
return arg
|
||||
|
||||
def next_offset(self, op, offset: int) -> int:
|
||||
def next_offset(self, op, offset):
|
||||
return xdis.next_offset(op, self.opc, offset)
|
||||
|
||||
def first_instr(self, start: int, end: int, instr, target=None, exact=True):
|
||||
def print_bytecode(self):
|
||||
for i in self.op_range(0, len(self.code)):
|
||||
op = self.code[i]
|
||||
if op in self.JUMP_OPS:
|
||||
dest = self.get_target(i, op)
|
||||
print("%i\t%s\t%i" % (i, self.opname[op], dest))
|
||||
else:
|
||||
print("%i\t%s\t" % (i, self.opname[op]))
|
||||
|
||||
def first_instr(self, start, end, instr, target=None, exact=True):
|
||||
"""
|
||||
Find the first <instr> in the block from start to end.
|
||||
<instr> is any python bytecode instruction or a list of opcodes
|
||||
@@ -368,9 +375,7 @@ class Scanner(ABC):
|
||||
result_offset = offset
|
||||
return result_offset
|
||||
|
||||
def last_instr(
|
||||
self, start: int, end: int, instr, target=None, exact=True
|
||||
) -> Optional[int]:
|
||||
def last_instr(self, start, end, instr, target=None, exact=True):
|
||||
"""
|
||||
Find the last <instr> in the block from start to end.
|
||||
<instr> is any python bytecode instruction or a list of opcodes
|
||||
@@ -466,9 +471,7 @@ class Scanner(ABC):
|
||||
# FIXME: this is broken on 3.6+. Replace remaining (2.x-based) calls
|
||||
# with inst_matches
|
||||
|
||||
def all_instr(
|
||||
self, start: int, end: int, instr, target=None, include_beyond_target=False
|
||||
):
|
||||
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
|
||||
"""
|
||||
Find all `instr` in the block from start to end.
|
||||
`instr` is any Python opcode or a list of opcodes
|
||||
@@ -593,26 +596,28 @@ class Scanner(ABC):
|
||||
def resetTokenClass(self):
|
||||
return self.setTokenClass(Token)
|
||||
|
||||
def restrict_to_parent(self, target: int, parent) -> int:
|
||||
def restrict_to_parent(self, target, parent):
|
||||
"""Restrict target to parent structure boundaries."""
|
||||
if not (parent["start"] < target < parent["end"]):
|
||||
target = parent["end"]
|
||||
return target
|
||||
|
||||
def setTokenClass(self, tokenClass: Token) -> Token:
|
||||
def setTokenClass(self, tokenClass):
|
||||
# assert isinstance(tokenClass, types.ClassType)
|
||||
self.Token = tokenClass
|
||||
return self.Token
|
||||
|
||||
|
||||
def get_scanner(version: Union[str, tuple], is_pypy=False, show_asm=None) -> Scanner:
|
||||
def get_scanner(version, is_pypy=False, show_asm=None):
|
||||
# If version is a string, turn that into the corresponding float.
|
||||
if isinstance(version, str):
|
||||
if version not in canonic_python_version:
|
||||
raise RuntimeError(f"Unknown Python version in xdis {version}")
|
||||
raise RuntimeError("Unknown Python version in xdis %s" % version)
|
||||
canonic_version = canonic_python_version[version]
|
||||
if canonic_version not in CANONIC2VERSION:
|
||||
raise RuntimeError(
|
||||
f"Unsupported Python version {version} (canonic {canonic_version})"
|
||||
"Unsupported Python version %s (canonic %s)"
|
||||
% (version, canonic_version)
|
||||
)
|
||||
version = CANONIC2VERSION[canonic_version]
|
||||
|
||||
@@ -651,7 +656,8 @@ def get_scanner(version: Union[str, tuple], is_pypy=False, show_asm=None) -> Sca
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"Unsupported Python version, {version_tuple_to_str(version)}, for decompilation"
|
||||
"Unsupported Python version, %s, for decompilation"
|
||||
% version_tuple_to_str(version)
|
||||
)
|
||||
return scanner
|
||||
|
||||
|
@@ -200,6 +200,7 @@ class Scanner2(Scanner):
|
||||
grammar rules. Specifically, variable arg tokens like MAKE_FUNCTION or BUILD_LIST
|
||||
cause specific rules for the specific number of arguments they take.
|
||||
"""
|
||||
|
||||
if not show_asm:
|
||||
show_asm = self.show_asm
|
||||
|
||||
@@ -1467,3 +1468,16 @@ class Scanner2(Scanner):
|
||||
instr_offsets = filtered
|
||||
filtered = []
|
||||
return instr_offsets
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import inspect
|
||||
|
||||
from xdis.version_info import PYTHON_VERSION_TRIPLE
|
||||
|
||||
co = inspect.currentframe().f_code
|
||||
|
||||
tokens, customize = Scanner2(PYTHON_VERSION_TRIPLE).ingest(co)
|
||||
for t in tokens:
|
||||
print(t)
|
||||
pass
|
||||
|
@@ -36,7 +36,6 @@ Finally we save token information.
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import xdis
|
||||
|
||||
@@ -214,7 +213,7 @@ class Scanner3(Scanner):
|
||||
t: Token,
|
||||
i: int,
|
||||
collection_type: str,
|
||||
) -> Optional[list]:
|
||||
):
|
||||
"""
|
||||
Try to a replace sequence of instruction that ends with a
|
||||
BUILD_xxx with a sequence that can be parsed much faster, but
|
||||
@@ -261,7 +260,7 @@ class Scanner3(Scanner):
|
||||
opname="COLLECTION_START",
|
||||
attr=collection_enum,
|
||||
pattr=collection_type,
|
||||
offset=f"{start_offset}_0",
|
||||
offset="%s_0" % start_offset,
|
||||
linestart=False,
|
||||
has_arg=True,
|
||||
has_extended_arg=False,
|
||||
@@ -285,7 +284,7 @@ class Scanner3(Scanner):
|
||||
)
|
||||
new_tokens.append(
|
||||
Token(
|
||||
opname=f"BUILD_{collection_type}",
|
||||
opname="BUILD_%s" % collection_type,
|
||||
attr=t.attr,
|
||||
pattr=t.pattr,
|
||||
offset=t.offset,
|
||||
@@ -300,7 +299,7 @@ class Scanner3(Scanner):
|
||||
|
||||
def bound_map_from_inst(
|
||||
self, insts: list, next_tokens: list, inst: Instruction, t: Token, i: int
|
||||
) -> Optional[list]:
|
||||
):
|
||||
"""
|
||||
Try to a sequence of instruction that ends with a BUILD_MAP into
|
||||
a sequence that can be parsed much faster, but inserting the
|
||||
@@ -337,7 +336,7 @@ class Scanner3(Scanner):
|
||||
opname="COLLECTION_START",
|
||||
attr=collection_enum,
|
||||
pattr="CONST_MAP",
|
||||
offset=f"{start_offset}_0",
|
||||
offset="%s_0" % start_offset,
|
||||
linestart=False,
|
||||
has_arg=True,
|
||||
has_extended_arg=False,
|
||||
@@ -386,9 +385,7 @@ class Scanner3(Scanner):
|
||||
)
|
||||
return new_tokens
|
||||
|
||||
def ingest(
|
||||
self, co, classname=None, code_objects={}, show_asm=None
|
||||
) -> Tuple[list, dict]:
|
||||
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
"""
|
||||
Create "tokens" the bytecode of an Python code object. Largely these
|
||||
are the opcode name, but in some cases that has been modified to make parsing
|
||||
@@ -519,7 +516,7 @@ class Scanner3(Scanner):
|
||||
else opname.split("_")[1]
|
||||
)
|
||||
try_tokens = self.bound_collection_from_inst(
|
||||
self.insts, new_tokens, inst, t, i, f"CONST_{collection_type}"
|
||||
self.insts, new_tokens, inst, t, i, "CONST_%s" % collection_type
|
||||
)
|
||||
if try_tokens is not None:
|
||||
new_tokens = try_tokens
|
||||
@@ -640,7 +637,7 @@ class Scanner3(Scanner):
|
||||
elif flags == 9:
|
||||
opname = "MAKE_FUNCTION_CLOSURE_POS"
|
||||
else:
|
||||
opname = f"MAKE_FUNCTION_{flags}"
|
||||
opname = "MAKE_FUNCTION_%d" % (flags)
|
||||
attr = []
|
||||
for flag in self.MAKE_FUNCTION_FLAGS:
|
||||
bit = flags & 1
|
||||
@@ -652,17 +649,21 @@ class Scanner3(Scanner):
|
||||
inst.argval
|
||||
)
|
||||
|
||||
pattr = f"{pos_args} positional, {name_pair_args} keyword only, {annotate_args} annotated"
|
||||
pattr = "%s positional, %s keyword only, %s annotated" % (
|
||||
pos_args,
|
||||
name_pair_args,
|
||||
annotate_args,
|
||||
)
|
||||
|
||||
if name_pair_args > 0 and annotate_args > 0:
|
||||
# FIXME: this should probably be K_
|
||||
opname += f"_N{name_pair_args}_A{annotate_args}"
|
||||
opname += "_N%s_A%s" % (name_pair_args, annotate_args)
|
||||
pass
|
||||
elif annotate_args > 0:
|
||||
opname += f"_A_{annotate_args}"
|
||||
opname += "_A_%s" % annotate_args
|
||||
pass
|
||||
elif name_pair_args > 0:
|
||||
opname += f"_N_{name_pair_args}"
|
||||
opname += "_N_%s" % name_pair_args
|
||||
pass
|
||||
else:
|
||||
# Rule customization mathics, MAKE_FUNCTION_...
|
||||
@@ -1546,16 +1547,12 @@ class Scanner3(Scanner):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import inspect
|
||||
|
||||
from xdis.version_info import PYTHON_VERSION_TRIPLE
|
||||
|
||||
if PYTHON_VERSION_TRIPLE >= (3, 2):
|
||||
import inspect
|
||||
co = inspect.currentframe().f_code
|
||||
|
||||
co = inspect.currentframe().f_code
|
||||
|
||||
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." % sys.version)
|
||||
pass
|
||||
tokens, customize = Scanner3(PYTHON_VERSION_TRIPLE).ingest(co)
|
||||
for t in tokens:
|
||||
print(t)
|
||||
|
@@ -22,13 +22,12 @@ This sets up opcodes Python's 3.7 and calls a generalized
|
||||
scanner routine for Python 3.
|
||||
"""
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_37 as opc
|
||||
|
||||
from uncompyle6.scanner import CONST_COLLECTIONS, Token
|
||||
from uncompyle6.scanner import CONST_COLLECTIONS
|
||||
from uncompyle6.scanners.scanner37base import Scanner37Base
|
||||
from uncompyle6.scanners.tok import Token
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPS from here
|
||||
JUMP_OPs = opc.JUMP_OPS
|
||||
@@ -85,7 +84,7 @@ class Scanner37(Scanner37Base):
|
||||
opname="COLLECTION_START",
|
||||
attr=collection_enum,
|
||||
pattr=collection_type,
|
||||
offset=f"{start_offset}_0",
|
||||
offset="%s_0" % start_offset,
|
||||
linestart=False,
|
||||
has_arg=True,
|
||||
has_extended_arg=False,
|
||||
@@ -109,7 +108,7 @@ class Scanner37(Scanner37Base):
|
||||
)
|
||||
new_tokens.append(
|
||||
Token(
|
||||
opname=f"BUILD_{collection_type}",
|
||||
opname="BUILD_%s" % collection_type,
|
||||
attr=t.attr,
|
||||
pattr=t.pattr,
|
||||
offset=t.offset,
|
||||
@@ -121,9 +120,7 @@ class Scanner37(Scanner37Base):
|
||||
)
|
||||
return new_tokens
|
||||
|
||||
def ingest(
|
||||
self, bytecode, classname=None, code_objects={}, show_asm=None
|
||||
) -> Tuple[list, dict]:
|
||||
def ingest(self, bytecode, classname=None, code_objects={}, show_asm=None):
|
||||
"""
|
||||
Create "tokens" the bytecode of an Python code object. Largely these
|
||||
are the opcode name, but in some cases that has been modified to make parsing
|
||||
@@ -159,7 +156,7 @@ class Scanner37(Scanner37Base):
|
||||
else t.kind.split("_")[1]
|
||||
)
|
||||
new_tokens = self.bound_collection_from_tokens(
|
||||
tokens, new_tokens, t, i, f"CONST_{collection_type}"
|
||||
tokens, new_tokens, t, i, "CONST_%s" % collection_type
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -197,4 +194,6 @@ if __name__ == "__main__":
|
||||
print(t.format())
|
||||
pass
|
||||
else:
|
||||
print(f"Need to be Python 3.7 to demo; I am version {version_tuple_to_str()}.")
|
||||
print(
|
||||
"Need to be Python 3.7 to demo; I am version %s." % version_tuple_to_str()
|
||||
)
|
||||
|
@@ -30,7 +30,6 @@ Finally we save token information.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from typing import Any, Dict, List, Set, Tuple
|
||||
|
||||
import xdis
|
||||
|
||||
@@ -48,9 +47,7 @@ CONST_COLLECTIONS = ("CONST_LIST", "CONST_SET", "CONST_DICT")
|
||||
|
||||
|
||||
class Scanner37Base(Scanner):
|
||||
def __init__(
|
||||
self, version: Tuple[int, int], show_asm=None, debug="", is_pypy=False
|
||||
):
|
||||
def __init__(self, version: tuple, show_asm=None, debug="", is_pypy=False):
|
||||
super(Scanner37Base, self).__init__(version, show_asm, is_pypy)
|
||||
self.offset2tok_index = None
|
||||
self.debug = debug
|
||||
@@ -560,17 +557,17 @@ class Scanner37Base(Scanner):
|
||||
self.structs = [{"type": "root", "start": 0, "end": n - 1}]
|
||||
|
||||
# All loop entry points
|
||||
self.loops: List[int] = []
|
||||
self.loops = []
|
||||
|
||||
# Map fixed jumps to their real destination
|
||||
self.fixed_jumps: Dict[int, int] = {}
|
||||
self.fixed_jumps = {}
|
||||
self.except_targets = {}
|
||||
self.ignore_if: Set[int] = set()
|
||||
self.ignore_if = set()
|
||||
self.build_statement_indices()
|
||||
|
||||
# Containers filled by detect_control_flow()
|
||||
self.not_continue: Set[int] = set()
|
||||
self.return_end_ifs: Set[int] = set()
|
||||
self.not_continue = set()
|
||||
self.return_end_ifs = set()
|
||||
self.setup_loop_targets = {} # target given setup_loop offset
|
||||
self.setup_loops = {} # setup_loop offset given target
|
||||
|
||||
@@ -708,9 +705,7 @@ class Scanner37Base(Scanner):
|
||||
# Finish filling the list for last statement
|
||||
slist += [codelen] * (codelen - len(slist))
|
||||
|
||||
def detect_control_flow(
|
||||
self, offset: int, targets: Dict[Any, Any], inst_index: int
|
||||
):
|
||||
def detect_control_flow(self, offset, targets, inst_index):
|
||||
"""
|
||||
Detect type of block structures and their boundaries to fix optimized jumps
|
||||
in python2.3+
|
||||
@@ -721,9 +716,9 @@ class Scanner37Base(Scanner):
|
||||
op = inst.opcode
|
||||
|
||||
# Detect parent structure
|
||||
parent: Dict[str, Any] = self.structs[0]
|
||||
start: int = parent["start"]
|
||||
end: int = parent["end"]
|
||||
parent = self.structs[0]
|
||||
start = parent["start"]
|
||||
end = parent["end"]
|
||||
|
||||
# Pick inner-most parent for our offset
|
||||
for struct in self.structs:
|
||||
@@ -958,7 +953,6 @@ if __name__ == "__main__":
|
||||
print(t)
|
||||
else:
|
||||
print(
|
||||
"Need to be Python 3.7..3.8 to demo; "
|
||||
f"I am version {version_tuple_to_str()}."
|
||||
"Need to be Python 3.7 to demo; I am version %s." % version_tuple_to_str()
|
||||
)
|
||||
pass
|
||||
|
@@ -22,8 +22,6 @@ This sets up opcodes Python's 3.8 and calls a generalized
|
||||
scanner routine for Python 3.7 and up.
|
||||
"""
|
||||
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from uncompyle6.scanners.tok import off2int
|
||||
from uncompyle6.scanners.scanner37 import Scanner37
|
||||
from uncompyle6.scanners.scanner37base import Scanner37Base
|
||||
@@ -45,7 +43,7 @@ class Scanner38(Scanner37):
|
||||
|
||||
def ingest(
|
||||
self, bytecode, classname=None, code_objects={}, show_asm=None
|
||||
) -> Tuple[list, dict]:
|
||||
) -> tuple:
|
||||
"""
|
||||
Create "tokens" the bytecode of an Python code object. Largely these
|
||||
are the opcode name, but in some cases that has been modified to make parsing
|
||||
@@ -73,7 +71,7 @@ class Scanner38(Scanner37):
|
||||
# The value is where the loop ends. In current Python,
|
||||
# JUMP_BACKS are always to loops. And blocks are ordered so that the
|
||||
# JUMP_BACK with the highest offset will be where the range ends.
|
||||
jump_back_targets: Dict[int, int] = {}
|
||||
jump_back_targets = {}
|
||||
for token in tokens:
|
||||
if token.kind == "JUMP_BACK":
|
||||
jump_back_targets[token.attr] = token.offset
|
||||
@@ -92,7 +90,7 @@ class Scanner38(Scanner37):
|
||||
if offset == next_end:
|
||||
loop_ends.pop()
|
||||
if self.debug:
|
||||
print(f"{' ' * len(loop_ends)}remove loop offset {offset}")
|
||||
print("%sremove loop offset %s" % (" " * len(loop_ends), offset))
|
||||
pass
|
||||
next_end = (
|
||||
loop_ends[-1]
|
||||
@@ -106,7 +104,8 @@ class Scanner38(Scanner37):
|
||||
next_end = off2int(jump_back_targets[offset], prefer_last=False)
|
||||
if self.debug:
|
||||
print(
|
||||
f"{' ' * len(loop_ends)}adding loop offset {offset} ending at {next_end}"
|
||||
"%sadding loop offset %s ending at %s"
|
||||
% (" " * len(loop_ends), offset, next_end)
|
||||
)
|
||||
loop_ends.append(next_end)
|
||||
|
||||
@@ -165,4 +164,4 @@ if __name__ == "__main__":
|
||||
print(t.format())
|
||||
pass
|
||||
else:
|
||||
print(f"Need to be Python 3.8 to demo; I am version {version_tuple_to_str()}.")
|
||||
print("Need to be Python 3.8 to demo; I am version %s." % version_tuple_to_str())
|
||||
|
@@ -19,7 +19,6 @@ import re
|
||||
import sys
|
||||
|
||||
intern = sys.intern
|
||||
from typing import Union
|
||||
|
||||
|
||||
def off2int(offset, prefer_last=True):
|
||||
@@ -61,7 +60,7 @@ class Token:
|
||||
opname,
|
||||
attr=None,
|
||||
pattr=None,
|
||||
offset: Union[int, str] = -1,
|
||||
offset=-1,
|
||||
linestart=None,
|
||||
op=None,
|
||||
has_arg=None,
|
||||
@@ -88,7 +87,7 @@ class Token:
|
||||
try:
|
||||
from xdis.std import _std_api
|
||||
except KeyError as e:
|
||||
print(f"I don't know about Python version {e} yet.")
|
||||
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 Exception:
|
||||
@@ -97,7 +96,7 @@ class Token:
|
||||
if version_tuple > (3, 9):
|
||||
print("Python versions 3.9 and greater are not supported.")
|
||||
else:
|
||||
print(f"xdis might need to be informed about version {e}")
|
||||
print("xdis might need to be informed about version {e}" % e)
|
||||
return
|
||||
|
||||
self.opc = _std_api.opc
|
||||
|
@@ -17,15 +17,15 @@
|
||||
"""
|
||||
|
||||
from uncompyle6.parsers.treenode import SyntaxTree
|
||||
from uncompyle6.scanners.tok import Token
|
||||
from uncompyle6.semantics.consts import (
|
||||
INDENT_PER_LEVEL,
|
||||
NO_PARENTHESIS_EVER,
|
||||
PRECEDENCE,
|
||||
TABLE_R,
|
||||
TABLE_DIRECT,
|
||||
TABLE_R,
|
||||
)
|
||||
from uncompyle6.semantics.helper import flatten_list
|
||||
from uncompyle6.scanners.tok import Token
|
||||
|
||||
|
||||
def customize_for_version(self, is_pypy, version):
|
||||
@@ -87,7 +87,7 @@ def customize_for_version(self, is_pypy, version):
|
||||
if line_number != self.line_number:
|
||||
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
|
||||
pass
|
||||
self.write(f"{sep}{value}")
|
||||
self.write("%s%s" % (sep, value))
|
||||
sep = ", "
|
||||
|
||||
assert n >= len(kwargs_names)
|
||||
@@ -101,7 +101,8 @@ def customize_for_version(self, is_pypy, version):
|
||||
if line_number != self.line_number:
|
||||
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
|
||||
pass
|
||||
self.write(f"{sep}{kwargs_names[i]}={value}")
|
||||
self.write(sep)
|
||||
self.write("%s=%s" % (kwargs_names[i], value))
|
||||
sep = ", "
|
||||
pass
|
||||
|
||||
|
@@ -339,7 +339,9 @@ def customize_for_version38(self, version):
|
||||
f_conversion = self.traverse(formatted_value, indent="")
|
||||
# Remove leaving "f" and quotes
|
||||
conversion = strip_quotes(f_conversion[1:])
|
||||
f_str = "f%s" % escape_string(f"{value_equal}{conversion}" + post_str)
|
||||
f_str = "f%s" % escape_string(
|
||||
("%s%s" % (value_equal, conversion)) + post_str
|
||||
)
|
||||
|
||||
self.write(f_str)
|
||||
self.in_format_string = old_in_format_string
|
||||
|
@@ -66,7 +66,6 @@ The node position 0 will be associated with "import".
|
||||
import re
|
||||
from bisect import bisect_right
|
||||
from collections import namedtuple
|
||||
from typing import Optional
|
||||
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
from spark_parser.ast import GenericASTTraversalPruningException
|
||||
@@ -2170,7 +2169,7 @@ def code_deparse_around_offset(
|
||||
offset,
|
||||
co,
|
||||
out=StringIO(),
|
||||
version: Optional[tuple] = None,
|
||||
version=None,
|
||||
is_pypy: bool = False,
|
||||
debug_opts=DEFAULT_DEBUG_OPTS,
|
||||
):
|
||||
@@ -2318,7 +2317,7 @@ def deparsed_find(tup, deparsed, code):
|
||||
# def test():
|
||||
# import os, sys
|
||||
|
||||
# def get_dups(li: list) -> set:
|
||||
# def get_dups(li: list):
|
||||
# dups = {}
|
||||
# for item in li:
|
||||
# dups[item] = dups.get(item, -1) + 1
|
||||
|
@@ -17,8 +17,6 @@ Generators and comprehension functions
|
||||
"""
|
||||
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from xdis import co_flags_is_async, iscode
|
||||
|
||||
from uncompyle6.parser import get_python_parser
|
||||
@@ -101,10 +99,10 @@ class ComprehensionMixin:
|
||||
def comprehension_walk(
|
||||
self,
|
||||
node,
|
||||
iter_index: Optional[int],
|
||||
code_index: int = -5,
|
||||
iter_index,
|
||||
code_index=-5,
|
||||
):
|
||||
p: int = self.prec
|
||||
p = self.prec
|
||||
self.prec = PRECEDENCE["lambda_body"] - 1
|
||||
|
||||
# FIXME: clean this up
|
||||
@@ -225,8 +223,8 @@ class ComprehensionMixin:
|
||||
def comprehension_walk_newer(
|
||||
self,
|
||||
node,
|
||||
iter_index: Optional[int],
|
||||
code_index: int = -5,
|
||||
iter_index,
|
||||
code_index=-5,
|
||||
collection_node=None,
|
||||
):
|
||||
"""Non-closure-based comprehensions the way they are done in Python3
|
||||
@@ -547,7 +545,7 @@ class ComprehensionMixin:
|
||||
pass
|
||||
self.prec = p
|
||||
|
||||
def get_comprehension_function(self, node, code_index: int):
|
||||
def get_comprehension_function(self, node, code_index):
|
||||
"""
|
||||
Build the body of a comprehension function and then
|
||||
find the comprehension node buried in the tree which may
|
||||
|
@@ -17,17 +17,18 @@
|
||||
All the crazy things we have to do to handle Python functions in Python before 3.0.
|
||||
The saga of changes continues in 3.0 and above and in other files.
|
||||
"""
|
||||
from typing import List, Tuple
|
||||
from uncompyle6.scanner import Code
|
||||
from uncompyle6.semantics.parser_error import ParserError
|
||||
from xdis import iscode
|
||||
|
||||
from uncompyle6.parser import ParserError as ParserError2
|
||||
from uncompyle6.scanner import Code
|
||||
from uncompyle6.semantics.helper import (
|
||||
print_docstring,
|
||||
find_all_globals,
|
||||
find_globals_and_nonlocals,
|
||||
find_none,
|
||||
print_docstring,
|
||||
)
|
||||
from xdis import iscode
|
||||
from uncompyle6.semantics.parser_error import ParserError
|
||||
|
||||
|
||||
def make_function1(self, node, is_lambda, nested=1, code_node=None):
|
||||
"""
|
||||
@@ -35,10 +36,10 @@ def make_function1(self, node, is_lambda, nested=1, code_node=None):
|
||||
This code is specialied for Python 2.
|
||||
"""
|
||||
|
||||
def build_param(tree, param_names: List[str]) -> Tuple[bool, List[str]]:
|
||||
def build_param(tree, param_names: list) -> tuple:
|
||||
"""build parameters:
|
||||
- handle defaults
|
||||
- handle format tuple parameters
|
||||
- handle defaults
|
||||
- handle format tuple parameters
|
||||
"""
|
||||
# if formal parameter is a tuple, the parameter name
|
||||
# starts with a dot (eg. '.1', '.2')
|
||||
@@ -187,5 +188,5 @@ def make_function1(self, node, is_lambda, nested=1, code_node=None):
|
||||
tree, code.co_name, code._customize, is_lambda=is_lambda, returnNone=rn
|
||||
)
|
||||
|
||||
code._tokens = None # save memory
|
||||
code._tokens = None # save memory
|
||||
code._customize = None # save memory
|
||||
|
@@ -226,8 +226,8 @@ class NonterminalActions:
|
||||
# from trepan.api import debug; debug()
|
||||
raise TypeError(
|
||||
(
|
||||
"Internal Error: n_const_list expects dict, list set, or set; got "
|
||||
f"{lastnodetype}"
|
||||
"Internal Error: n_const_list expects dict, list set, or set; got %s"
|
||||
% lastnodetype
|
||||
)
|
||||
)
|
||||
|
||||
@@ -263,7 +263,7 @@ class NonterminalActions:
|
||||
line_len = len(next_indent)
|
||||
sep += "\n" + next_indent
|
||||
|
||||
sep_key_value = f"{sep}{repr(keys[i])}: {value}"
|
||||
sep_key_value = "%s %s: %s" % (sep, repr(keys[i]), value)
|
||||
line_len += len(sep_key_value)
|
||||
self.write(sep_key_value)
|
||||
sep = ", "
|
||||
|
@@ -131,7 +131,6 @@ Python.
|
||||
|
||||
import sys
|
||||
from io import StringIO
|
||||
from typing import Optional
|
||||
|
||||
from spark_parser import GenericASTTraversal
|
||||
from xdis import COMPILER_FLAG_BIT, IS_PYPY, iscode
|
||||
@@ -342,18 +341,18 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
||||
stream.write(self.str_with_template1(ast, "", None))
|
||||
stream.write("\n")
|
||||
|
||||
def str_with_template1(self, ast, indent, sibNum=None) -> str:
|
||||
def str_with_template1(self, ast, indent, sibNum=None):
|
||||
rv = str(ast.kind)
|
||||
|
||||
if sibNum is not None:
|
||||
rv = "%2d. %s" % (sibNum, rv)
|
||||
enumerate_children = False
|
||||
if len(ast) > 1:
|
||||
rv += f" ({len(ast)})"
|
||||
rv += " (%d)" % (len(ast))
|
||||
enumerate_children = True
|
||||
|
||||
if ast in PRECEDENCE:
|
||||
rv += f", precedence {PRECEDENCE[ast]}"
|
||||
rv += ", precedence %s" % PRECEDENCE[ast]
|
||||
|
||||
mapping = self._get_mapping(ast)
|
||||
table = mapping[0]
|
||||
@@ -782,10 +781,11 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
||||
node[index]
|
||||
except IndexError:
|
||||
raise RuntimeError(
|
||||
f"""
|
||||
Expanding '{node.kind}' in template '{entry}[{arg}]':
|
||||
{index} is invalid; has only {len(node)} entries
|
||||
"""
|
||||
Expanding '%s' in template '%s[%s]':
|
||||
%s is invalid; has only %d entries
|
||||
"""
|
||||
% (node.kind, entry, arg, index, len(node))
|
||||
)
|
||||
self.preorder(node[index])
|
||||
|
||||
@@ -807,9 +807,13 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
||||
node[index].kind,
|
||||
)
|
||||
else:
|
||||
assert node[tup[0]] in tup[1], (
|
||||
f"at {node.kind}[{tup[0]}], expected to be in '{tup[1]}' "
|
||||
f"node; got '{node[tup[0]].kind}'"
|
||||
assert (
|
||||
node[tup[0]] in tup[1]
|
||||
), "at %s[%d], expected to be in '%s' node; got '%s'" % (
|
||||
node.kind,
|
||||
arg,
|
||||
index[1],
|
||||
node[index[0]].kind,
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -1017,7 +1021,7 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
||||
result = "(%s)" % result
|
||||
return result
|
||||
# return self.traverse(node[1])
|
||||
return f"({name}"
|
||||
return "(" + name
|
||||
|
||||
def build_class(self, code):
|
||||
"""Dump class definition, doc string and class body."""
|
||||
@@ -1280,7 +1284,7 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
|
||||
def code_deparse(
|
||||
co,
|
||||
out=sys.stdout,
|
||||
version: Optional[tuple] = None,
|
||||
version=None,
|
||||
debug_opts=DEFAULT_DEBUG_OPTS,
|
||||
code_objects={},
|
||||
compile_mode="exec",
|
||||
@@ -1288,7 +1292,7 @@ def code_deparse(
|
||||
walker=SourceWalker,
|
||||
start_offset: int = 0,
|
||||
stop_offset: int = -1,
|
||||
) -> Optional[SourceWalker]:
|
||||
):
|
||||
"""
|
||||
ingests and deparses a given code block 'co'. If version is None,
|
||||
we will use the current Python interpreter version.
|
||||
@@ -1371,9 +1375,11 @@ def code_deparse(
|
||||
expected_start = None
|
||||
|
||||
if expected_start:
|
||||
assert deparsed.ast == expected_start, (
|
||||
f"Should have parsed grammar start to '{expected_start}'; "
|
||||
f"got: {deparsed.ast.kind}"
|
||||
assert (
|
||||
deparsed.ast == expected_start
|
||||
), "Should have parsed grammar start to '%s'; got: %s" % (
|
||||
expected_start,
|
||||
deparsed.ast.kind,
|
||||
)
|
||||
# save memory
|
||||
del tokens
|
||||
|
@@ -14,7 +14,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from copy import copy
|
||||
from typing import Optional
|
||||
|
||||
from spark_parser import GenericASTTraversal, GenericASTTraversalPruningException
|
||||
|
||||
@@ -73,7 +72,7 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
self,
|
||||
version: tuple,
|
||||
is_pypy=False,
|
||||
show_ast: Optional[dict] = None,
|
||||
show_ast=None,
|
||||
):
|
||||
self.version = version
|
||||
self.showast = show_ast
|
||||
|
@@ -14,4 +14,4 @@
|
||||
# This file is suitable for sourcing inside POSIX shell as
|
||||
# well as importing into Python
|
||||
# fmt: off
|
||||
__version__="3.9.1.dev0" # noqa
|
||||
__version__="3.9.1" # noqa
|
||||
|
Reference in New Issue
Block a user