| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- # Mode: -*- python -*-
- # Copyright (c) 2015-2021 by Rocky Bernstein <rb@dustyfeet.com>
- #
- # Note: we can't start with #! because setup.py bdist_wheel will look for that
- # and change that into something that's not portable. Thank you, Python!
- #
- #
- from __future__ import print_function
- import os
- import os.path as osp
- import sys
- import click
- from xdis import disassemble_file
- from xdis.version import __version__
- from xdis.version_info import PYTHON_VERSION_STR, PYTHON_VERSION_TRIPLE
- program, ext = os.path.splitext(os.path.basename(__file__))
- PATTERNS = ("*.pyc", "*.pyo")
- if click.__version__ >= "7.":
- case_sensitive = {"case_sensitive": False}
- else:
- case_sensitive = {}
- @click.command()
- @click.option(
- "--format",
- "-F",
- type=click.Choice(
- ["xasm", "bytes", "classic", "dis", "extended", "extended-bytes", "header"],
- **case_sensitive
- ),
- help="Select disassembly style.",
- )
- @click.option(
- "--method",
- "-m",
- metavar="FUNCTION-OR-METHOD",
- multiple=True,
- type=str,
- help=("Specify which specific methods or functions to show. "
- "If omitted all, functions are shown. "
- "Can be given multiple times.")
- )
- @click.option(
- "--show-source/--no-show-source",
- "-S",
- help="Intersperse Python source text from linecache if available.",
- )
- @click.version_option(version=__version__)
- @click.argument("files", nargs=-1, type=click.Path(readable=True), required=True)
- def main(format: list[str], method: tuple, show_source: bool, files):
- """Disassembles a Python bytecode file.
- We handle bytecode for virtually every release of Python and some releases of PyPy.
- The version of Python in the bytecode doesn't have to be the same version as
- the Python interpreter used to run this program. For example, you can disassemble Python 3.6.9
- bytecode from Python 2.7.15 and vice versa.
- """
- if not ((2, 7) <= PYTHON_VERSION_TRIPLE < (3, 16)):
- mess = "This code works on 3.6 to 3.15; you have %s."
- if (2, 4) <= PYTHON_VERSION_TRIPLE <= (2, 7):
- mess += " Code that works for %s can be found in the python-2.4 branch\n"
- elif (3, 1) <= PYTHON_VERSION_TRIPLE <= (3, 2):
- mess += " Code that works for %s can be found in the python-3.1 branch\n"
- elif (3, 3) <= PYTHON_VERSION_TRIPLE <= (3, 5):
- mess += " Code that works for %s can be found in the python-3.3 branch\n"
- sys.stderr.write(mess % PYTHON_VERSION_STR)
- sys.exit(2)
- rc = 0
- for path in files:
- # Some sanity checks
- if not osp.exists(path):
- sys.stderr.write("File name: '%s' doesn't exist\n" % path)
- continue
- elif not osp.isfile(path):
- sys.stderr.write("File name: '%s' isn't a file\n" % path)
- continue
- elif osp.getsize(path) < 50 and not path.endswith(".py"):
- sys.stderr.write(
- "File name: '%s (%d bytes)' is too short to be a valid pyc file\n"
- % (path, osp.getsize(path))
- )
- continue
- try:
- disassemble_file(path, sys.stdout, format, show_source=show_source, methods=method)
- except (ImportError, NotImplementedError, ValueError) as e:
- print(e)
- rc = 3
- sys.exit(rc)
- if __name__ == "__main__":
- main(sys.argv[1:])
|