Coder Social home page Coder Social logo

astunparse's People

Contributors

amundhov avatar cav71 avatar fjarri avatar graingert avatar hroncok avatar jwilk avatar mbdevpl avatar nvbn avatar serge-sans-paille avatar simonpercivall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

astunparse's Issues

Compatibility with 3.7

Hello, it seems that astunparse is not compatible with python 3.7, because async is now a reserved python word.

Reproduction:

$ docker run --rm python:3.7 bash -c 'pip install astunparse; python -c "import astunparse"'
Collecting astunparse
  Downloading https://files.pythonhosted.org/packages/8b/ea/d38686f1718e307d83673d905dcffd142640a31a217e4e76d1de78f21b20/astunparse-1.5.0-py2.py3-none-any.whl
Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.7/site-packages (from astunparse) (0.31.1)
Collecting six<2.0,>=1.6.1 (from astunparse)
  Downloading https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl
Installing collected packages: six, astunparse
Successfully installed astunparse-1.5.0 six-1.11.0
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/astunparse/__init__.py", line 4, in <module>
    from .unparser import Unparser
  File "/usr/local/lib/python3.7/site-packages/astunparse/unparser.py", line 331
    def _generic_FunctionDef(self, t, async=False):
                                          ^
SyntaxError: invalid syntax

Better support for docstrings

Thanks for a useful library!

astunparse doesn't special case docstrings:

>>> print(astunparse.unparse(ast.parse('''def f(): """multi\n    line\n    docstring\n    """ ''')))

def f():
    'multi\n    line\n    docstring\n    '

whereas Python 3.9 handles it more idiomatically:

>>> print(ast.unparse(ast.parse('''def f(): """multi\n    line\n    docstring\n    """ ''')))

def f():
    """multi
    line
    docstring
    """

Note the implementation in Python 3.9 also handles other issues, like #38, better.

More generally, I was wondering if there's a plan to attempt to use the Python 3.9 implementation. Let me know if this is something you want and if you would want help!

Skipped test for Python 3.6 is valid code

This test fails (if you unskip it) for Python 3.6 and 3.7 (but works for Python 3.8, presumably because of ast.Constant changes?)

@unittest.skipUnless(sys.version_info < (3, 6), "Only works for Python < 3.6")
    def test_integer_parens(self):
        self.check_roundtrip("3 .__abs__()")

The issue is astunparse doesn't add back in the necessary space:

Python 3.6.10 (default, Jan 23 2020, 23:35:52) 
[GCC 4.2.1 Compatible Apple LLVM 11.0.0 (clang-1100.0.33.17)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 3 .__abs__()
3
>>> 3.__abs__()
  File "<stdin>", line 1
    3.__abs__()
            ^
SyntaxError: invalid syntax
>>> import ast
>>> import astunparse
>>> astunparse.unparse(ast.parse("3 .__abs__()"))
'\n3.__abs__()\n'

Clarify Python version compatibility

README mentions support for Python 2.6 through 3.5, however astunparse appears to have Python 3.8 support. I noticed, however, that there don't appear to be any tests for the walrus operator or positional-only args.

incorrect unparsing of nested f-strings

The code f'''-{f"""*{f"+{f'.{x}.'}+"}*"""}-''' is valid, but after roundtrip parsing+unparsing with ast and astunparse, the result is f'-{f\'*{f"+{f\\\'.{x}.\\\'}+"}*\'}-' which is not valid anymore.

The nested f-strings need to use a different string limiters, therefore 4-level nesting is possible, but with astunparse latest version 1.6.0 such structures will generate errors.

In [1]: import ast

In [2]: code = '''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\''''

In [3]: x = 5

In [4]: eval(code)
Out[4]: '-*+.5.+*-'

In [5]: syntax = ast.parse(code)

In [6]: import astunparse

In [7]: unparsed = astunparse.unparse(syntax)

In [8]: reparsed = ast.parse(unparsed)
Traceback (most recent call last):

  File "/home/mateusz/.local/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2910, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "<ipython-input-8-aecaafbb06ba>", line 1, in <module>
    reparsed = ast.parse(unparsed)

  File "/home/mateusz/.local/lib/python3.6/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)

  File "<unknown>", line 2
SyntaxError: f-string expression part cannot include a backslash

In [9]: print(unparsed)

f'-{f\'*{f"+{f\\\'.{x}.\\\'}+"}*\'}-'

internal tests failing under Python 3.11

While Python 3.11 is now the default Python version within the upcoming Debian release bookworm it turns out that the package astunparse can't run the internal tests since the switch to Python 3.11. This has become visible within a complete rebuild of all package within the archive.

There is an issue raised due the package rebuild problem against the package in astunparse in the Debian BTS within #1026643

The relevant part about the issue should be

> I: pybuild base:240: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.11_astunparse/build; python3.11 -m pytest tests
> ============================= test session starts ==============================
> platform linux -- Python 3.11.1, pytest-7.2.0, pluggy-1.0.0+repack
> rootdir: /<<PKGBUILDDIR>>
> collected 94 items
> 
> tests/test_dump.py ........................s.s.s......s...........       [ 50%]
> tests/test_unparse.py .................F......s.s.s......s...........    [100%]
> 
> =================================== FAILURES ===================================
> __________________________ UnparseTestCase.test_files __________________________
> 
> self = <tests.test_unparse.UnparseTestCase testMethod=test_files>
> 
>     def test_files(self):
>         names = []
>         for test_dir in self.test_directories:
>             for n in os.listdir(test_dir):
>                 if n.endswith('.py') and not n.startswith('bad'):
>                     names.append(os.path.join(test_dir, n))
>     
>         for filename in names:
>             print('Testing %s' % filename)
>             source = read_pyfile(filename)
> >           self.check_roundtrip(source)
> 
> tests/common.py:192: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> tests/test_unparse.py:18: in check_roundtrip
>     code2 = astunparse.unparse(ast1)
> astunparse/__init__.py:13: in unparse
>     Unparser(tree, file=v)
> astunparse/unparser.py:38: in __init__
>     self.dispatch(tree)
> astunparse/unparser.py:66: in dispatch
>     meth(tree)
> astunparse/unparser.py:78: in _Module
>     self.dispatch(stmt)
> astunparse/unparser.py:66: in dispatch
>     meth(tree)
> astunparse/unparser.py:347: in _FunctionDef
>     self.__FunctionDef_helper(t, "def")
> astunparse/unparser.py:365: in __FunctionDef_helper
>     self.dispatch(t.body)
> astunparse/unparser.py:63: in dispatch
>     self.dispatch(t)
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <astunparse.unparser.Unparser object at 0x7f5def4f5090>
> tree = <ast.Match object at 0x7f5def4cdb40>
> 
>     def dispatch(self, tree):
>         "Dispatcher function, dispatching tree type T to method _T."
>         if isinstance(tree, list):
>             for t in tree:
>                 self.dispatch(t)
>             return
> >       meth = getattr(self, "_"+tree.__class__.__name__)
> E       AttributeError: 'Unparser' object has no attribute '_Match'
> 
> astunparse/unparser.py:65: AttributeError
> Testing /usr/lib/python3.11/abc.py
> ... [snip]
> Testing /usr/lib/python3.11/traceback.py
> =============================== warnings summary ===============================
> .pybuild/cpython3_3.11_astunparse/build/tests/test_unparse.py::UnparseTestCase::test_files
>   internal:134: DeprecationWarning: invalid escape sequence '\Z'
> 
> .pybuild/cpython3_3.11_astunparse/build/tests/test_unparse.py::UnparseTestCase::test_files
>   internal:59: DeprecationWarning: invalid escape sequence '\P'
> 
> .pybuild/cpython3_3.11_astunparse/build/tests/test_unparse.py::UnparseTestCase::test_files
>   internal:166: DeprecationWarning: invalid escape sequence '\P'
> 
> .pybuild/cpython3_3.11_astunparse/build/tests/test_unparse.py::UnparseTestCase::test_files
>   internal:166: DeprecationWarning: invalid escape sequence '\s'
> 
> -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
> =========================== short test summary info ============================
> FAILED tests/test_unparse.py::UnparseTestCase::test_files - AttributeError: '...

Found out by some T&E it seems that the issue is triggered by usr/lib/python3.11/traceback.py and at least /usr/lib/python3.11/dataclasses.py too.

I hereby would like to get some help to fix the problem.
Currently there a two patches applied to the source of version 1.6.3 before the package is build.
https://sources.debian.org/src/astunparse/1.6.3-1/debian/patches/

Thanks!

Docstrings are unparsed as simple string values

# coding=utf-8
""" Test. """

import ast
import inspect

import astunparse


def x():
    """
    Test
    """
    pass


if __name__ == '__main__':
    p = ast.parse(inspect.getsource(x))
    r = astunparse.unparse(p)
    print(r)

becomes

def x():
    '\n    Test\n    '
    pass

'astunparse.unparse' error with Python 3.8.1

Hi,

The following code throws an error when it parses a constant inside some_code:

astunparse.unparse(some_code)

The traceback is:

  File "test_astunparse.py", line 236, in process_code
    result = astunparse.unparse(some_code)
  File "C:\Python38\lib\site-packages\astunparse\__init__.py", line 13, in unparse
    Unparser(tree, file=v)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 38, in __init__
    self.dispatch(tree)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 66, in dispatch
    meth(tree)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 78, in _Module
    self.dispatch(stmt)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 66, in dispatch
    meth(tree)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 347, in _FunctionDef
    self.__FunctionDef_helper(t, "def")
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 359, in __FunctionDef_helper
    self.dispatch(t.args)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 66, in dispatch
    meth(tree)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 791, in _arguments
    self.dispatch(d)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 66, in dispatch
    meth(tree)
  File "C:\Python38\lib\site-packages\astunparse\unparser.py", line 551, in _Constant
    if t.kind == "u":
AttributeError: 'Constant' object has no attribute 'kind'

A hack that fixes this is to comment out line 551 and 552 in C:\Python38\lib\site-packages\astunparse\unparser.py, like so:

        elif value is Ellipsis: # instead of `...` for Py2 compatibility
            self.write("...")
        else:
            # if t.kind == "u":
            #    self.write("u")
            self._write_constant(t.value)

Regards

unit tests are executed many times

I've just noticed that unit test execution takes 30 seconds on my machine. It's because executing unit tests of astunparse executes also tests of Python libraries!

Looking at Travis CI log: https://travis-ci.org/simonpercivall/astunparse/jobs/102925518

The list of tests (tooooo long):

test_annotations (tests.test_dump.DirectoryTestCase) ... ok
test_bytes (tests.test_dump.DirectoryTestCase) ... ok
test_chained_comparisons (tests.test_dump.DirectoryTestCase) ... ok
test_class_decorators (tests.test_dump.DirectoryTestCase) ... ok
test_class_definition (tests.test_dump.DirectoryTestCase) ... ok
test_del_statement (tests.test_dump.DirectoryTestCase) ... ok
test_dict_comprehension (tests.test_dump.DirectoryTestCase) ... ok
test_elifs (tests.test_dump.DirectoryTestCase) ... ok
test_files (tests.test_dump.DirectoryTestCase) ... Testing /opt/python/3.5.0/lib/python3.5/zipfile.py
Testing /opt/python/3.5.0/lib/python3.5/pydoc.py
Testing /opt/python/3.5.0/lib/python3.5/tempfile.py
Testing /opt/python/3.5.0/lib/python3.5/mailcap.py
Testing /opt/python/3.5.0/lib/python3.5/stat.py
Testing /opt/python/3.5.0/lib/python3.5/mailbox.py
Testing /opt/python/3.5.0/lib/python3.5/difflib.py
Testing /opt/python/3.5.0/lib/python3.5/contextlib.py
Testing /opt/python/3.5.0/lib/python3.5/site.py
Testing /opt/python/3.5.0/lib/python3.5/doctest.py
Testing /opt/python/3.5.0/lib/python3.5/_dummy_thread.py
Testing /opt/python/3.5.0/lib/python3.5/_sitebuiltins.py
Testing /opt/python/3.5.0/lib/python3.5/weakref.py
Testing /opt/python/3.5.0/lib/python3.5/nntplib.py
Testing /opt/python/3.5.0/lib/python3.5/stringprep.py
Testing /opt/python/3.5.0/lib/python3.5/__future__.py
Testing /opt/python/3.5.0/lib/python3.5/ipaddress.py
Testing /opt/python/3.5.0/lib/python3.5/os.py
Testing /opt/python/3.5.0/lib/python3.5/ftplib.py
Testing /opt/python/3.5.0/lib/python3.5/gzip.py
Testing /opt/python/3.5.0/lib/python3.5/re.py
Testing /opt/python/3.5.0/lib/python3.5/cProfile.py
Testing /opt/python/3.5.0/lib/python3.5/dummy_threading.py
Testing /opt/python/3.5.0/lib/python3.5/telnetlib.py
Testing /opt/python/3.5.0/lib/python3.5/argparse.py
Testing /opt/python/3.5.0/lib/python3.5/tarfile.py
Testing /opt/python/3.5.0/lib/python3.5/copy.py
Testing /opt/python/3.5.0/lib/python3.5/abc.py
Testing /opt/python/3.5.0/lib/python3.5/keyword.py
Testing /opt/python/3.5.0/lib/python3.5/aifc.py
Testing /opt/python/3.5.0/lib/python3.5/socket.py
Testing /opt/python/3.5.0/lib/python3.5/cgitb.py
Testing /opt/python/3.5.0/lib/python3.5/sched.py
Testing /opt/python/3.5.0/lib/python3.5/glob.py
Testing /opt/python/3.5.0/lib/python3.5/pkgutil.py
Testing /opt/python/3.5.0/lib/python3.5/uu.py
Testing /opt/python/3.5.0/lib/python3.5/textwrap.py
Testing /opt/python/3.5.0/lib/python3.5/socketserver.py
Testing /opt/python/3.5.0/lib/python3.5/operator.py
Testing /opt/python/3.5.0/lib/python3.5/io.py
Testing /opt/python/3.5.0/lib/python3.5/bz2.py
Testing /opt/python/3.5.0/lib/python3.5/_sysconfigdata.py
Testing /opt/python/3.5.0/lib/python3.5/runpy.py
Testing /opt/python/3.5.0/lib/python3.5/turtle.py
Testing /opt/python/3.5.0/lib/python3.5/zipapp.py
Testing /opt/python/3.5.0/lib/python3.5/ssl.py
Testing /opt/python/3.5.0/lib/python3.5/netrc.py
Testing /opt/python/3.5.0/lib/python3.5/code.py
Testing /opt/python/3.5.0/lib/python3.5/symbol.py
Testing /opt/python/3.5.0/lib/python3.5/plistlib.py
Testing /opt/python/3.5.0/lib/python3.5/_pydecimal.py
Testing /opt/python/3.5.0/lib/python3.5/reprlib.py
Testing /opt/python/3.5.0/lib/python3.5/crypt.py
Testing /opt/python/3.5.0/lib/python3.5/datetime.py
Testing /opt/python/3.5.0/lib/python3.5/formatter.py
Testing /opt/python/3.5.0/lib/python3.5/inspect.py
Testing /opt/python/3.5.0/lib/python3.5/tty.py
Testing /opt/python/3.5.0/lib/python3.5/signal.py
Testing /opt/python/3.5.0/lib/python3.5/shelve.py
Testing /opt/python/3.5.0/lib/python3.5/decimal.py
Testing /opt/python/3.5.0/lib/python3.5/lzma.py
Testing /opt/python/3.5.0/lib/python3.5/colorsys.py
Testing /opt/python/3.5.0/lib/python3.5/_compression.py
Testing /opt/python/3.5.0/lib/python3.5/smtplib.py
Testing /opt/python/3.5.0/lib/python3.5/imp.py
Testing /opt/python/3.5.0/lib/python3.5/symtable.py
Testing /opt/python/3.5.0/lib/python3.5/imghdr.py
Testing /opt/python/3.5.0/lib/python3.5/ast.py
Testing /opt/python/3.5.0/lib/python3.5/nturl2path.py
Testing /opt/python/3.5.0/lib/python3.5/subprocess.py
Testing /opt/python/3.5.0/lib/python3.5/heapq.py
Testing /opt/python/3.5.0/lib/python3.5/pyclbr.py
Testing /opt/python/3.5.0/lib/python3.5/this.py
Testing /opt/python/3.5.0/lib/python3.5/antigravity.py
Testing /opt/python/3.5.0/lib/python3.5/binhex.py
Testing /opt/python/3.5.0/lib/python3.5/_weakrefset.py
Testing /opt/python/3.5.0/lib/python3.5/trace.py
Testing /opt/python/3.5.0/lib/python3.5/tabnanny.py
Testing /opt/python/3.5.0/lib/python3.5/random.py
Testing /opt/python/3.5.0/lib/python3.5/uuid.py
Testing /opt/python/3.5.0/lib/python3.5/tokenize.py
Testing /opt/python/3.5.0/lib/python3.5/macpath.py
Testing /opt/python/3.5.0/lib/python3.5/bisect.py
Testing /opt/python/3.5.0/lib/python3.5/timeit.py
Testing /opt/python/3.5.0/lib/python3.5/_strptime.py
Testing /opt/python/3.5.0/lib/python3.5/mimetypes.py
Testing /opt/python/3.5.0/lib/python3.5/pprint.py
Testing /opt/python/3.5.0/lib/python3.5/pdb.py
Testing /opt/python/3.5.0/lib/python3.5/numbers.py
Testing /opt/python/3.5.0/lib/python3.5/sre_compile.py
Testing /opt/python/3.5.0/lib/python3.5/traceback.py
Testing /opt/python/3.5.0/lib/python3.5/getpass.py
Testing /opt/python/3.5.0/lib/python3.5/fnmatch.py
Testing /opt/python/3.5.0/lib/python3.5/tracemalloc.py
Testing /opt/python/3.5.0/lib/python3.5/optparse.py
Testing /opt/python/3.5.0/lib/python3.5/rlcompleter.py
Testing /opt/python/3.5.0/lib/python3.5/pickle.py
Testing /opt/python/3.5.0/lib/python3.5/pty.py
Testing /opt/python/3.5.0/lib/python3.5/imaplib.py
Testing /opt/python/3.5.0/lib/python3.5/pipes.py
Testing /opt/python/3.5.0/lib/python3.5/quopri.py
Testing /opt/python/3.5.0/lib/python3.5/_compat_pickle.py
Testing /opt/python/3.5.0/lib/python3.5/platform.py
Testing /opt/python/3.5.0/lib/python3.5/calendar.py
Testing /opt/python/3.5.0/lib/python3.5/shutil.py
Testing /opt/python/3.5.0/lib/python3.5/_markupbase.py
Testing /opt/python/3.5.0/lib/python3.5/hmac.py
Testing /opt/python/3.5.0/lib/python3.5/_pyio.py
Testing /opt/python/3.5.0/lib/python3.5/sndhdr.py
Testing /opt/python/3.5.0/lib/python3.5/xdrlib.py
Testing /opt/python/3.5.0/lib/python3.5/fractions.py
Testing /opt/python/3.5.0/lib/python3.5/chunk.py
Testing /opt/python/3.5.0/lib/python3.5/types.py
Testing /opt/python/3.5.0/lib/python3.5/_threading_local.py
Testing /opt/python/3.5.0/lib/python3.5/token.py
Testing /opt/python/3.5.0/lib/python3.5/asynchat.py
Testing /opt/python/3.5.0/lib/python3.5/opcode.py
Testing /opt/python/3.5.0/lib/python3.5/sunau.py
Testing /opt/python/3.5.0/lib/python3.5/compileall.py
Testing /opt/python/3.5.0/lib/python3.5/smtpd.py
Testing /opt/python/3.5.0/lib/python3.5/cmd.py
Testing /opt/python/3.5.0/lib/python3.5/sysconfig.py
Testing /opt/python/3.5.0/lib/python3.5/wave.py
Testing /opt/python/3.5.0/lib/python3.5/functools.py
Testing /opt/python/3.5.0/lib/python3.5/bdb.py
Testing /opt/python/3.5.0/lib/python3.5/sre_constants.py
Testing /opt/python/3.5.0/lib/python3.5/modulefinder.py
Testing /opt/python/3.5.0/lib/python3.5/warnings.py
Testing /opt/python/3.5.0/lib/python3.5/filecmp.py
Testing /opt/python/3.5.0/lib/python3.5/sre_parse.py
Testing /opt/python/3.5.0/lib/python3.5/codeop.py
Testing /opt/python/3.5.0/lib/python3.5/posixpath.py
Testing /opt/python/3.5.0/lib/python3.5/pstats.py
Testing /opt/python/3.5.0/lib/python3.5/string.py
Testing /opt/python/3.5.0/lib/python3.5/csv.py
Testing /opt/python/3.5.0/lib/python3.5/ntpath.py
Testing /opt/python/3.5.0/lib/python3.5/pathlib.py
Testing /opt/python/3.5.0/lib/python3.5/webbrowser.py
Testing /opt/python/3.5.0/lib/python3.5/selectors.py
Testing /opt/python/3.5.0/lib/python3.5/typing.py
Testing /opt/python/3.5.0/lib/python3.5/configparser.py
Testing /opt/python/3.5.0/lib/python3.5/asyncore.py
Testing /opt/python/3.5.0/lib/python3.5/linecache.py
Testing /opt/python/3.5.0/lib/python3.5/pickletools.py
Testing /opt/python/3.5.0/lib/python3.5/fileinput.py
Testing /opt/python/3.5.0/lib/python3.5/_osx_support.py
Testing /opt/python/3.5.0/lib/python3.5/genericpath.py
Testing /opt/python/3.5.0/lib/python3.5/copyreg.py
Testing /opt/python/3.5.0/lib/python3.5/cgi.py
Testing /opt/python/3.5.0/lib/python3.5/_bootlocale.py
Testing /opt/python/3.5.0/lib/python3.5/gettext.py
Testing /opt/python/3.5.0/lib/python3.5/locale.py
Testing /opt/python/3.5.0/lib/python3.5/shlex.py
Testing /opt/python/3.5.0/lib/python3.5/profile.py
Testing /opt/python/3.5.0/lib/python3.5/poplib.py
Testing /opt/python/3.5.0/lib/python3.5/dis.py
Testing /opt/python/3.5.0/lib/python3.5/statistics.py
Testing /opt/python/3.5.0/lib/python3.5/struct.py
Testing /opt/python/3.5.0/lib/python3.5/base64.py
Testing /opt/python/3.5.0/lib/python3.5/queue.py
Testing /opt/python/3.5.0/lib/python3.5/enum.py
Testing /opt/python/3.5.0/lib/python3.5/hashlib.py
Testing /opt/python/3.5.0/lib/python3.5/__phello__.foo.py
Testing /opt/python/3.5.0/lib/python3.5/getopt.py
Testing /opt/python/3.5.0/lib/python3.5/macurl2path.py
Testing /opt/python/3.5.0/lib/python3.5/_collections_abc.py
Testing /opt/python/3.5.0/lib/python3.5/py_compile.py
Testing /opt/python/3.5.0/lib/python3.5/threading.py
Testing /opt/python/3.5.0/lib/python3.5/codecs.py
ok
test_for_else (tests.test_dump.DirectoryTestCase) ... ok
test_function_arguments (tests.test_dump.DirectoryTestCase) ... ok
test_huge_float (tests.test_dump.DirectoryTestCase) ... ok
test_imaginary_literals (tests.test_dump.DirectoryTestCase) ... ok
test_import_many (tests.test_dump.DirectoryTestCase) ... ok
test_integer_parens (tests.test_dump.DirectoryTestCase) ... ok
test_lambda_parentheses (tests.test_dump.DirectoryTestCase) ... ok
test_min_int27 (tests.test_dump.DirectoryTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_dump.DirectoryTestCase) ... ok
test_negative_zero (tests.test_dump.DirectoryTestCase) ... ok
test_nonlocal (tests.test_dump.DirectoryTestCase) ... ok
test_raise_from (tests.test_dump.DirectoryTestCase) ... ok
test_relative_import (tests.test_dump.DirectoryTestCase) ... ok
test_repr (tests.test_dump.DirectoryTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_dump.DirectoryTestCase) ... ok
test_set_literal (tests.test_dump.DirectoryTestCase) ... ok
test_shifts (tests.test_dump.DirectoryTestCase) ... ok
test_starred_assignment (tests.test_dump.DirectoryTestCase) ... ok
test_try_except_finally (tests.test_dump.DirectoryTestCase) ... ok
test_unary_parens (tests.test_dump.DirectoryTestCase) ... ok
test_while_else (tests.test_dump.DirectoryTestCase) ... ok
test_with_as (tests.test_dump.DirectoryTestCase) ... ok
test_with_simple (tests.test_dump.DirectoryTestCase) ... ok
test_with_two_items (tests.test_dump.DirectoryTestCase) ... ok
test_annotations (tests.test_dump.DumpTestCase) ... ok
test_bytes (tests.test_dump.DumpTestCase) ... ok
test_chained_comparisons (tests.test_dump.DumpTestCase) ... ok
test_class_decorators (tests.test_dump.DumpTestCase) ... ok
test_class_definition (tests.test_dump.DumpTestCase) ... ok
test_del_statement (tests.test_dump.DumpTestCase) ... ok
test_dict_comprehension (tests.test_dump.DumpTestCase) ... ok
test_elifs (tests.test_dump.DumpTestCase) ... ok
test_for_else (tests.test_dump.DumpTestCase) ... ok
test_function_arguments (tests.test_dump.DumpTestCase) ... ok
test_huge_float (tests.test_dump.DumpTestCase) ... ok
test_imaginary_literals (tests.test_dump.DumpTestCase) ... ok
test_import_many (tests.test_dump.DumpTestCase) ... ok
test_integer_parens (tests.test_dump.DumpTestCase) ... ok
test_lambda_parentheses (tests.test_dump.DumpTestCase) ... ok
test_min_int27 (tests.test_dump.DumpTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_dump.DumpTestCase) ... ok
test_negative_zero (tests.test_dump.DumpTestCase) ... ok
test_nonlocal (tests.test_dump.DumpTestCase) ... ok
test_raise_from (tests.test_dump.DumpTestCase) ... ok
test_relative_import (tests.test_dump.DumpTestCase) ... ok
test_repr (tests.test_dump.DumpTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_dump.DumpTestCase) ... ok
test_set_literal (tests.test_dump.DumpTestCase) ... ok
test_shifts (tests.test_dump.DumpTestCase) ... ok
test_starred_assignment (tests.test_dump.DumpTestCase) ... ok
test_try_except_finally (tests.test_dump.DumpTestCase) ... ok
test_unary_parens (tests.test_dump.DumpTestCase) ... ok
test_while_else (tests.test_dump.DumpTestCase) ... ok
test_with_as (tests.test_dump.DumpTestCase) ... ok
test_with_simple (tests.test_dump.DumpTestCase) ... ok
test_with_two_items (tests.test_dump.DumpTestCase) ... ok
test_annotations (tests.test_unparse.UnparseTestCase) ... ok
test_bytes (tests.test_unparse.UnparseTestCase) ... ok
test_chained_comparisons (tests.test_unparse.UnparseTestCase) ... ok
test_class_decorators (tests.test_unparse.UnparseTestCase) ... ok
test_class_definition (tests.test_unparse.UnparseTestCase) ... ok
test_del_statement (tests.test_unparse.UnparseTestCase) ... ok
test_dict_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_elifs (tests.test_unparse.UnparseTestCase) ... ok
test_for_else (tests.test_unparse.UnparseTestCase) ... ok
test_function_arguments (tests.test_unparse.UnparseTestCase) ... ok
test_huge_float (tests.test_unparse.UnparseTestCase) ... ok
test_imaginary_literals (tests.test_unparse.UnparseTestCase) ... ok
test_import_many (tests.test_unparse.UnparseTestCase) ... ok
test_integer_parens (tests.test_unparse.UnparseTestCase) ... ok
test_lambda_parentheses (tests.test_unparse.UnparseTestCase) ... ok
test_min_int27 (tests.test_unparse.UnparseTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_unparse.UnparseTestCase) ... ok
test_negative_zero (tests.test_unparse.UnparseTestCase) ... ok
test_nonlocal (tests.test_unparse.UnparseTestCase) ... ok
test_raise_from (tests.test_unparse.UnparseTestCase) ... ok
test_relative_import (tests.test_unparse.UnparseTestCase) ... ok
test_repr (tests.test_unparse.UnparseTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_set_literal (tests.test_unparse.UnparseTestCase) ... ok
test_shifts (tests.test_unparse.UnparseTestCase) ... ok
test_starred_assignment (tests.test_unparse.UnparseTestCase) ... ok
test_try_except_finally (tests.test_unparse.UnparseTestCase) ... ok
test_unary_parens (tests.test_unparse.UnparseTestCase) ... ok
test_while_else (tests.test_unparse.UnparseTestCase) ... ok
test_with_as (tests.test_unparse.UnparseTestCase) ... ok
test_with_simple (tests.test_unparse.UnparseTestCase) ... ok
test_with_two_items (tests.test_unparse.UnparseTestCase) ... ok
test_files (tests.test_unparse.DirectoryTestCase) ... Testing /opt/python/3.5.0/lib/python3.5/zipfile.py
Testing /opt/python/3.5.0/lib/python3.5/pydoc.py
Testing /opt/python/3.5.0/lib/python3.5/tempfile.py
Testing /opt/python/3.5.0/lib/python3.5/mailcap.py
Testing /opt/python/3.5.0/lib/python3.5/stat.py
Testing /opt/python/3.5.0/lib/python3.5/mailbox.py
Testing /opt/python/3.5.0/lib/python3.5/difflib.py
Testing /opt/python/3.5.0/lib/python3.5/contextlib.py
Testing /opt/python/3.5.0/lib/python3.5/site.py
Testing /opt/python/3.5.0/lib/python3.5/doctest.py
Testing /opt/python/3.5.0/lib/python3.5/_dummy_thread.py
Testing /opt/python/3.5.0/lib/python3.5/_sitebuiltins.py
Testing /opt/python/3.5.0/lib/python3.5/weakref.py
Testing /opt/python/3.5.0/lib/python3.5/nntplib.py
Testing /opt/python/3.5.0/lib/python3.5/stringprep.py
Testing /opt/python/3.5.0/lib/python3.5/__future__.py
Testing /opt/python/3.5.0/lib/python3.5/ipaddress.py
Testing /opt/python/3.5.0/lib/python3.5/os.py
Testing /opt/python/3.5.0/lib/python3.5/ftplib.py
Testing /opt/python/3.5.0/lib/python3.5/gzip.py
Testing /opt/python/3.5.0/lib/python3.5/re.py
Testing /opt/python/3.5.0/lib/python3.5/cProfile.py
Testing /opt/python/3.5.0/lib/python3.5/dummy_threading.py
Testing /opt/python/3.5.0/lib/python3.5/telnetlib.py
Testing /opt/python/3.5.0/lib/python3.5/argparse.py
Testing /opt/python/3.5.0/lib/python3.5/tarfile.py
Testing /opt/python/3.5.0/lib/python3.5/copy.py
Testing /opt/python/3.5.0/lib/python3.5/abc.py
Testing /opt/python/3.5.0/lib/python3.5/keyword.py
Testing /opt/python/3.5.0/lib/python3.5/aifc.py
Testing /opt/python/3.5.0/lib/python3.5/socket.py
Testing /opt/python/3.5.0/lib/python3.5/cgitb.py
Testing /opt/python/3.5.0/lib/python3.5/sched.py
Testing /opt/python/3.5.0/lib/python3.5/glob.py
Testing /opt/python/3.5.0/lib/python3.5/pkgutil.py
Testing /opt/python/3.5.0/lib/python3.5/uu.py
Testing /opt/python/3.5.0/lib/python3.5/textwrap.py
Testing /opt/python/3.5.0/lib/python3.5/socketserver.py
Testing /opt/python/3.5.0/lib/python3.5/operator.py
Testing /opt/python/3.5.0/lib/python3.5/io.py
Testing /opt/python/3.5.0/lib/python3.5/bz2.py
Testing /opt/python/3.5.0/lib/python3.5/_sysconfigdata.py
Testing /opt/python/3.5.0/lib/python3.5/runpy.py
Testing /opt/python/3.5.0/lib/python3.5/turtle.py
Testing /opt/python/3.5.0/lib/python3.5/zipapp.py
Testing /opt/python/3.5.0/lib/python3.5/ssl.py
Testing /opt/python/3.5.0/lib/python3.5/netrc.py
Testing /opt/python/3.5.0/lib/python3.5/code.py
Testing /opt/python/3.5.0/lib/python3.5/symbol.py
Testing /opt/python/3.5.0/lib/python3.5/plistlib.py
Testing /opt/python/3.5.0/lib/python3.5/_pydecimal.py
Testing /opt/python/3.5.0/lib/python3.5/reprlib.py
Testing /opt/python/3.5.0/lib/python3.5/crypt.py
Testing /opt/python/3.5.0/lib/python3.5/datetime.py
Testing /opt/python/3.5.0/lib/python3.5/formatter.py
Testing /opt/python/3.5.0/lib/python3.5/inspect.py
Testing /opt/python/3.5.0/lib/python3.5/tty.py
Testing /opt/python/3.5.0/lib/python3.5/signal.py
Testing /opt/python/3.5.0/lib/python3.5/shelve.py
Testing /opt/python/3.5.0/lib/python3.5/decimal.py
Testing /opt/python/3.5.0/lib/python3.5/lzma.py
Testing /opt/python/3.5.0/lib/python3.5/colorsys.py
Testing /opt/python/3.5.0/lib/python3.5/_compression.py
Testing /opt/python/3.5.0/lib/python3.5/smtplib.py
Testing /opt/python/3.5.0/lib/python3.5/imp.py
Testing /opt/python/3.5.0/lib/python3.5/symtable.py
Testing /opt/python/3.5.0/lib/python3.5/imghdr.py
Testing /opt/python/3.5.0/lib/python3.5/ast.py
Testing /opt/python/3.5.0/lib/python3.5/nturl2path.py
Testing /opt/python/3.5.0/lib/python3.5/subprocess.py
Testing /opt/python/3.5.0/lib/python3.5/heapq.py
Testing /opt/python/3.5.0/lib/python3.5/pyclbr.py
Testing /opt/python/3.5.0/lib/python3.5/this.py
Testing /opt/python/3.5.0/lib/python3.5/antigravity.py
Testing /opt/python/3.5.0/lib/python3.5/binhex.py
Testing /opt/python/3.5.0/lib/python3.5/_weakrefset.py
Testing /opt/python/3.5.0/lib/python3.5/trace.py
Testing /opt/python/3.5.0/lib/python3.5/tabnanny.py
Testing /opt/python/3.5.0/lib/python3.5/random.py
Testing /opt/python/3.5.0/lib/python3.5/uuid.py
Testing /opt/python/3.5.0/lib/python3.5/tokenize.py
Testing /opt/python/3.5.0/lib/python3.5/macpath.py
Testing /opt/python/3.5.0/lib/python3.5/bisect.py
Testing /opt/python/3.5.0/lib/python3.5/timeit.py
Testing /opt/python/3.5.0/lib/python3.5/_strptime.py
Testing /opt/python/3.5.0/lib/python3.5/mimetypes.py
Testing /opt/python/3.5.0/lib/python3.5/pprint.py
Testing /opt/python/3.5.0/lib/python3.5/pdb.py
Testing /opt/python/3.5.0/lib/python3.5/numbers.py
Testing /opt/python/3.5.0/lib/python3.5/sre_compile.py
Testing /opt/python/3.5.0/lib/python3.5/traceback.py
Testing /opt/python/3.5.0/lib/python3.5/getpass.py
Testing /opt/python/3.5.0/lib/python3.5/fnmatch.py
Testing /opt/python/3.5.0/lib/python3.5/tracemalloc.py
Testing /opt/python/3.5.0/lib/python3.5/optparse.py
Testing /opt/python/3.5.0/lib/python3.5/rlcompleter.py
Testing /opt/python/3.5.0/lib/python3.5/pickle.py
Testing /opt/python/3.5.0/lib/python3.5/pty.py
Testing /opt/python/3.5.0/lib/python3.5/imaplib.py
Testing /opt/python/3.5.0/lib/python3.5/pipes.py
Testing /opt/python/3.5.0/lib/python3.5/quopri.py
Testing /opt/python/3.5.0/lib/python3.5/_compat_pickle.py
Testing /opt/python/3.5.0/lib/python3.5/platform.py
Testing /opt/python/3.5.0/lib/python3.5/calendar.py
Testing /opt/python/3.5.0/lib/python3.5/shutil.py
Testing /opt/python/3.5.0/lib/python3.5/_markupbase.py
Testing /opt/python/3.5.0/lib/python3.5/hmac.py
Testing /opt/python/3.5.0/lib/python3.5/_pyio.py
Testing /opt/python/3.5.0/lib/python3.5/sndhdr.py
Testing /opt/python/3.5.0/lib/python3.5/xdrlib.py
Testing /opt/python/3.5.0/lib/python3.5/fractions.py
Testing /opt/python/3.5.0/lib/python3.5/chunk.py
Testing /opt/python/3.5.0/lib/python3.5/types.py
Testing /opt/python/3.5.0/lib/python3.5/_threading_local.py
Testing /opt/python/3.5.0/lib/python3.5/token.py
Testing /opt/python/3.5.0/lib/python3.5/asynchat.py
Testing /opt/python/3.5.0/lib/python3.5/opcode.py
Testing /opt/python/3.5.0/lib/python3.5/sunau.py
Testing /opt/python/3.5.0/lib/python3.5/compileall.py
Testing /opt/python/3.5.0/lib/python3.5/smtpd.py
Testing /opt/python/3.5.0/lib/python3.5/cmd.py
Testing /opt/python/3.5.0/lib/python3.5/sysconfig.py
Testing /opt/python/3.5.0/lib/python3.5/wave.py
Testing /opt/python/3.5.0/lib/python3.5/functools.py
Testing /opt/python/3.5.0/lib/python3.5/bdb.py
Testing /opt/python/3.5.0/lib/python3.5/sre_constants.py
Testing /opt/python/3.5.0/lib/python3.5/modulefinder.py
Testing /opt/python/3.5.0/lib/python3.5/warnings.py
Testing /opt/python/3.5.0/lib/python3.5/filecmp.py
Testing /opt/python/3.5.0/lib/python3.5/sre_parse.py
Testing /opt/python/3.5.0/lib/python3.5/codeop.py
Testing /opt/python/3.5.0/lib/python3.5/posixpath.py
Testing /opt/python/3.5.0/lib/python3.5/pstats.py
Testing /opt/python/3.5.0/lib/python3.5/string.py
Testing /opt/python/3.5.0/lib/python3.5/csv.py
Testing /opt/python/3.5.0/lib/python3.5/ntpath.py
Testing /opt/python/3.5.0/lib/python3.5/pathlib.py
Testing /opt/python/3.5.0/lib/python3.5/webbrowser.py
Testing /opt/python/3.5.0/lib/python3.5/selectors.py
Testing /opt/python/3.5.0/lib/python3.5/typing.py
Testing /opt/python/3.5.0/lib/python3.5/configparser.py
Testing /opt/python/3.5.0/lib/python3.5/asyncore.py
Testing /opt/python/3.5.0/lib/python3.5/linecache.py
Testing /opt/python/3.5.0/lib/python3.5/pickletools.py
Testing /opt/python/3.5.0/lib/python3.5/fileinput.py
Testing /opt/python/3.5.0/lib/python3.5/_osx_support.py
Testing /opt/python/3.5.0/lib/python3.5/genericpath.py
Testing /opt/python/3.5.0/lib/python3.5/copyreg.py
Testing /opt/python/3.5.0/lib/python3.5/cgi.py
Testing /opt/python/3.5.0/lib/python3.5/_bootlocale.py
Testing /opt/python/3.5.0/lib/python3.5/gettext.py
Testing /opt/python/3.5.0/lib/python3.5/locale.py
Testing /opt/python/3.5.0/lib/python3.5/shlex.py
Testing /opt/python/3.5.0/lib/python3.5/profile.py
Testing /opt/python/3.5.0/lib/python3.5/poplib.py
Testing /opt/python/3.5.0/lib/python3.5/dis.py
Testing /opt/python/3.5.0/lib/python3.5/statistics.py
Testing /opt/python/3.5.0/lib/python3.5/struct.py
Testing /opt/python/3.5.0/lib/python3.5/base64.py
Testing /opt/python/3.5.0/lib/python3.5/queue.py
Testing /opt/python/3.5.0/lib/python3.5/enum.py
Testing /opt/python/3.5.0/lib/python3.5/hashlib.py
Testing /opt/python/3.5.0/lib/python3.5/__phello__.foo.py
Testing /opt/python/3.5.0/lib/python3.5/getopt.py
Testing /opt/python/3.5.0/lib/python3.5/macurl2path.py
Testing /opt/python/3.5.0/lib/python3.5/_collections_abc.py
Testing /opt/python/3.5.0/lib/python3.5/py_compile.py
Testing /opt/python/3.5.0/lib/python3.5/threading.py
Testing /opt/python/3.5.0/lib/python3.5/codecs.py
ok
test_annotations (tests.test_unparse.UnparseTestCase) ... ok
test_bytes (tests.test_unparse.UnparseTestCase) ... ok
test_chained_comparisons (tests.test_unparse.UnparseTestCase) ... ok
test_class_decorators (tests.test_unparse.UnparseTestCase) ... ok
test_class_definition (tests.test_unparse.UnparseTestCase) ... ok
test_del_statement (tests.test_unparse.UnparseTestCase) ... ok
test_dict_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_elifs (tests.test_unparse.UnparseTestCase) ... ok
test_for_else (tests.test_unparse.UnparseTestCase) ... ok
test_function_arguments (tests.test_unparse.UnparseTestCase) ... ok
test_huge_float (tests.test_unparse.UnparseTestCase) ... ok
test_imaginary_literals (tests.test_unparse.UnparseTestCase) ... ok
test_import_many (tests.test_unparse.UnparseTestCase) ... ok
test_integer_parens (tests.test_unparse.UnparseTestCase) ... ok
test_lambda_parentheses (tests.test_unparse.UnparseTestCase) ... ok
test_min_int27 (tests.test_unparse.UnparseTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_unparse.UnparseTestCase) ... ok
test_negative_zero (tests.test_unparse.UnparseTestCase) ... ok
test_nonlocal (tests.test_unparse.UnparseTestCase) ... ok
test_raise_from (tests.test_unparse.UnparseTestCase) ... ok
test_relative_import (tests.test_unparse.UnparseTestCase) ... ok
test_repr (tests.test_unparse.UnparseTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_set_literal (tests.test_unparse.UnparseTestCase) ... ok
test_shifts (tests.test_unparse.UnparseTestCase) ... ok
test_starred_assignment (tests.test_unparse.UnparseTestCase) ... ok
test_try_except_finally (tests.test_unparse.UnparseTestCase) ... ok
test_unary_parens (tests.test_unparse.UnparseTestCase) ... ok
test_while_else (tests.test_unparse.UnparseTestCase) ... ok
test_with_as (tests.test_unparse.UnparseTestCase) ... ok
test_with_simple (tests.test_unparse.UnparseTestCase) ... ok
test_with_two_items (tests.test_unparse.UnparseTestCase) ... ok

v1.6.1 not tagged on GitHub

Hi. I want to package astunparse to Fedora, as it is a transitive dependency of Pythran.

I'd like to run the tests when I build the package. The tests are however missing form the sdist tarball on PyPI, see #33

So I wanted to use the tagged tarball from GtiHub instead, but the most recent version 1.6.1 is not tagged. Could you please tag it?

Incorrect unparsing of extended slices in Python 3.9

Repro:

Python 3.9.2 (default, Mar  3 2021, 20:02:32) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import astunparse
>>> import ast
>>> n = ast.parse('return foo(x)[:, tf.newaxis] - foo(x)[tf.newaxis, :]')
>>> astunparse.unparse(n)
'\nreturn (foo(x)[(:, tf.newaxis)] - foo(x)[(tf.newaxis, :)])\n'
>>> ast.parse(_)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/mdan/miniconda3/envs/py39test/lib/python3.9/ast.py", line 50, in parse
    return compile(source, filename, mode, flags,
  File "<unknown>", line 2
    return (foo(x)[(:, tf.newaxis)] - foo(x)[(tf.newaxis, :)])
                    ^
SyntaxError: invalid syntax
>>> 

It seems that the extra parens inserted around the slice are at fault. They do not appear in Python 3.7.

Broken on Python 3.5

Python 3.5 made some changes to the ast representation (this already broke pytest: pytest-dev/pytest#744).

The initial error I'm getting is: AttributeError: 'Call' object has no attribute 'starargs'

Escape issue

import ast
import astunparse
exp = 'like_duration = like(extra, \'%\\"duration\\\\\\":%\')'
print(exp)
# like_duration = like(extra, '%\"duration\\\":%')
print(ast.dump(ast.parse(exp)))
# Module(body=[Assign(targets=[Name(id='like_duration', ctx=Store())], value=Call(func=Name(id='like', ctx=Load()), args=[Name(id='extra', ctx=Load()), Str(s='%"duration\\":%')], keywords=[]))])
print(astunparse.unparse(ast.parse(exp)))
# like_duration = like(extra, '%"duration\\":%')
# expect: like_duration = like(extra, '%\"duration\\\":%')

Format strings with complex forrmat specifiers are not unparsed correctly

In the description of PEP 498, there is an example of a format specifier that itself contains interpolated values:

https://www.python.org/dev/peps/pep-0498/#format-specifiers

>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal('12.34567')
>>> f'result: {value:{width}.{precision}}'

The unparse method doesn't seem to handle this well:

>>> astunparse.unparse(ast.parse("f'result: {value:{width}.{precision}}'"))
"\nf'''result: {value:f'''{width}.{precision}'''}'''\n"

Notice that the part after value: is not bare but rather inside a format string, which isn't valid. The correct behavior should be:

>>> astunparse.unparse(ast.parse("f'result: {value:{width}.{precision}}'"))
"\nf'''result: {value:{width}.{precision}}'''\n"

release bdist_wheel

It looks like your setup.cfg and makefile support bdist_wheel but none are on PyPI.

Test issues with Python 3.9

Hello. In Fedora development version, we have recently updated to Python 3.9.0b1. Here are some test failures we get w/astunparse:

$ tox -e py39
py39 develop-inst-noop: .../astunparse
py39 installed: -e git://github.com/simonpercivall/astunparse.git@2acce01fcdda2ea32eea835c30ccca21aaff7297#egg=astunparse,coverage==3.7.1,six==1.15.0
py39 run-test-pre: PYTHONHASHSEED='209768785'
py39 run-test: commands[0] | .../astunparse/.tox/py39/bin/python setup.py test
running test
WARNING: Testing via this command is deprecated and will be removed in a future version. Users looking for a generic test entry point independent of test runner are encouraged to use tox.
running egg_info
writing lib/astunparse.egg-info/PKG-INFO
writing dependency_links to lib/astunparse.egg-info/dependency_links.txt
writing requirements to lib/astunparse.egg-info/requires.txt
writing top-level names to lib/astunparse.egg-info/top_level.txt
reading manifest file 'lib/astunparse.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'lib/astunparse.egg-info/SOURCES.txt'
running build_ext
test_annotations (tests.test_unparse.UnparseTestCase) ... ok
test_async_comp_and_gen_in_async_function (tests.test_unparse.UnparseTestCase) ... ok
test_async_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_async_for (tests.test_unparse.UnparseTestCase) ... ok
test_async_function_def (tests.test_unparse.UnparseTestCase) ... ok
test_async_generator_expression (tests.test_unparse.UnparseTestCase) ... ok
test_async_with (tests.test_unparse.UnparseTestCase) ... ok
test_async_with_as (tests.test_unparse.UnparseTestCase) ... ok
test_bytes (tests.test_unparse.UnparseTestCase) ... ok
test_chained_comparisons (tests.test_unparse.UnparseTestCase) ... ok
test_class_decorators (tests.test_unparse.UnparseTestCase) ... ok
test_class_definition (tests.test_unparse.UnparseTestCase) ... ok
test_complex_f_string (tests.test_unparse.UnparseTestCase) ... ok
test_del_statement (tests.test_unparse.UnparseTestCase) ... ok
test_dict_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_dict_with_unpacking (tests.test_unparse.UnparseTestCase) ... ok
test_elifs (tests.test_unparse.UnparseTestCase) ... ok
test_files (tests.test_unparse.UnparseTestCase) ... ok
test_for_else (tests.test_unparse.UnparseTestCase) ... ok
test_formatted_value (tests.test_unparse.UnparseTestCase) ... ok
test_function_arguments (tests.test_unparse.UnparseTestCase) ... ok
test_huge_float (tests.test_unparse.UnparseTestCase) ... ok
test_imaginary_literals (tests.test_unparse.UnparseTestCase) ... ok
test_import_many (tests.test_unparse.UnparseTestCase) ... ok
test_integer_parens (tests.test_unparse.UnparseTestCase) ... skipped 'Only works for Python < 3.6'
test_joined_str (tests.test_unparse.UnparseTestCase) ... ok
test_joined_str_361 (tests.test_unparse.UnparseTestCase) ... skipped 'Only supported on 3.6.0'
test_lambda_parentheses (tests.test_unparse.UnparseTestCase) ... ok
test_min_int27 (tests.test_unparse.UnparseTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_unparse.UnparseTestCase) ... ok
test_negative_zero (tests.test_unparse.UnparseTestCase) ... ok
test_nonlocal (tests.test_unparse.UnparseTestCase) ... ok
test_parser_modes (tests.test_unparse.UnparseTestCase) ... ok
test_raise_from (tests.test_unparse.UnparseTestCase) ... ok
test_relative_import (tests.test_unparse.UnparseTestCase) ... ok
test_repr (tests.test_unparse.UnparseTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_unparse.UnparseTestCase) ... ok
test_set_literal (tests.test_unparse.UnparseTestCase) ... ok
test_shifts (tests.test_unparse.UnparseTestCase) ... ok
test_starred_assignment (tests.test_unparse.UnparseTestCase) ... ok
test_try_except_finally (tests.test_unparse.UnparseTestCase) ... ok
test_unary_parens (tests.test_unparse.UnparseTestCase) ... ok
test_variable_annotation (tests.test_unparse.UnparseTestCase) ... ok
test_while_else (tests.test_unparse.UnparseTestCase) ... ok
test_with_as (tests.test_unparse.UnparseTestCase) ... ok
test_with_simple (tests.test_unparse.UnparseTestCase) ... ok
test_with_two_items (tests.test_unparse.UnparseTestCase) ... ok
test_annotations (tests.test_dump.DumpTestCase) ... FAIL
test_async_comp_and_gen_in_async_function (tests.test_dump.DumpTestCase) ... FAIL
test_async_comprehension (tests.test_dump.DumpTestCase) ... FAIL
test_async_for (tests.test_dump.DumpTestCase) ... FAIL
test_async_function_def (tests.test_dump.DumpTestCase) ... FAIL
test_async_generator_expression (tests.test_dump.DumpTestCase) ... FAIL
test_async_with (tests.test_dump.DumpTestCase) ... FAIL
test_async_with_as (tests.test_dump.DumpTestCase) ... FAIL
test_bytes (tests.test_dump.DumpTestCase) ... FAIL
test_chained_comparisons (tests.test_dump.DumpTestCase) ... FAIL
test_class_decorators (tests.test_dump.DumpTestCase) ... ok
test_class_definition (tests.test_dump.DumpTestCase) ... FAIL
test_complex_f_string (tests.test_dump.DumpTestCase) ... FAIL
test_del_statement (tests.test_dump.DumpTestCase) ... ok
test_dict_comprehension (tests.test_dump.DumpTestCase) ... FAIL
test_dict_with_unpacking (tests.test_dump.DumpTestCase) ... ok
test_elifs (tests.test_dump.DumpTestCase) ... ok
test_files (tests.test_dump.DumpTestCase) ... ok
test_for_else (tests.test_dump.DumpTestCase) ... FAIL
test_formatted_value (tests.test_dump.DumpTestCase) ... FAIL
test_function_arguments (tests.test_dump.DumpTestCase) ... FAIL
test_huge_float (tests.test_dump.DumpTestCase) ... FAIL
test_imaginary_literals (tests.test_dump.DumpTestCase) ... FAIL
test_import_many (tests.test_dump.DumpTestCase) ... FAIL
test_integer_parens (tests.test_dump.DumpTestCase) ... skipped 'Only works for Python < 3.6'
test_joined_str (tests.test_dump.DumpTestCase) ... FAIL
test_joined_str_361 (tests.test_dump.DumpTestCase) ... skipped 'Only supported on 3.6.0'
test_lambda_parentheses (tests.test_dump.DumpTestCase) ... FAIL
test_min_int27 (tests.test_dump.DumpTestCase) ... skipped 'Only works for Python 2'
test_min_int30 (tests.test_dump.DumpTestCase) ... FAIL
test_negative_zero (tests.test_dump.DumpTestCase) ... FAIL
test_nonlocal (tests.test_dump.DumpTestCase) ... FAIL
test_parser_modes (tests.test_dump.DumpTestCase) ... ok
test_raise_from (tests.test_dump.DumpTestCase) ... FAIL
test_relative_import (tests.test_dump.DumpTestCase) ... FAIL
test_repr (tests.test_dump.DumpTestCase) ... skipped 'Only for Python 2'
test_set_comprehension (tests.test_dump.DumpTestCase) ... FAIL
test_set_literal (tests.test_dump.DumpTestCase) ... FAIL
test_shifts (tests.test_dump.DumpTestCase) ... FAIL
test_starred_assignment (tests.test_dump.DumpTestCase) ... FAIL
test_try_except_finally (tests.test_dump.DumpTestCase) ... FAIL
test_unary_parens (tests.test_dump.DumpTestCase) ... FAIL
test_variable_annotation (tests.test_dump.DumpTestCase) ... FAIL
test_while_else (tests.test_dump.DumpTestCase) ... FAIL
test_with_as (tests.test_dump.DumpTestCase) ... FAIL
test_with_simple (tests.test_dump.DumpTestCase) ... FAIL
test_with_two_items (tests.test_dump.DumpTestCase) ... FAIL

======================================================================
FAIL: test_annotations (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 331, in test_annotations
    self.check_roundtrip("def f(a : int): pass")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[113 chars]ad()), type_comment=None)], vararg=None, kwonl[133 chars]=[])" != "Modu[113 chars]ad()))], kwonlyargs=[], kw_defaults=[], defaul[56 chars]=[])"
Diff is 1031 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_comp_and_gen_in_async_function (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 356, in test_async_comp_and_gen_in_async_function
    self.check_roundtrip(async_comprehensions_and_generators)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[84 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[1944 chars]=[])" != "Modu[84 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[1725 chars]=[])"
Diff is 3952 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_comprehension (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 360, in test_async_comprehension
    self.check_roundtrip("{i async for i in aiter() if i % 2}")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[247 chars]stant(value=2, kind=None))], is_async=1)]))], type_ignores=[])" != "Modu[247 chars]stant(value=2))], is_async=1)]))], type_ignores=[])"
Diff is 1175 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_for (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 418, in test_async_for
    self.check_roundtrip(async_for)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[71 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[262 chars]=[])" != "Modu[71 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[185 chars]=[])"
Diff is 704 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_function_def (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 414, in test_async_function_def
    self.check_roundtrip(async_function_def)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[71 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[145 chars]=[])" != "Modu[71 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[87 chars]=[])"
Diff is 949 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_generator_expression (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 366, in test_async_generator_expression
    self.check_roundtrip("(i ** 2 async for i in agen())")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[101 chars]lue=2, kind=None)), generators=[comprehension([138 chars]=[])" != "Modu[101 chars]lue=2)), generators=[comprehension(target=Name[127 chars]=[])"
- Module(body=[Expr(value=GeneratorExp(elt=BinOp(left=Name(id='i', ctx=Load()), op=Pow(), right=Constant(value=2, kind=None)), generators=[comprehension(target=Name(id='i', ctx=Store()), iter=Call(func=Name(id='agen', ctx=Load()), args=[], keywords=[]), ifs=[], is_async=1)]))], type_ignores=[])
+ Module(body=[Expr(value=GeneratorExp(elt=BinOp(left=Name(id='i', ctx=Load()), op=Pow(), right=Constant(value=2)), generators=[comprehension(target=Name(id='i', ctx=Store()), iter=Call(func=Name(id='agen', ctx=Load()), args=[], keywords=[]), ifs=[], is_async=1)]))], type_ignores=[])


======================================================================
FAIL: test_async_with (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 422, in test_async_with
    self.check_roundtrip(async_with)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[71 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[292 chars]=[])" != "Modu[71 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[195 chars]=[])"
Diff is 744 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_async_with_as (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 426, in test_async_with_as
    self.check_roundtrip(async_with_as)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[71 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[313 chars]=[])" != "Modu[71 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[236 chars]=[])"
Diff is 806 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_bytes (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 294, in test_bytes
    self.check_roundtrip("b'123'")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Module(body=[Expr(value=Constant(value=b'123', kind=None))], type_ignores=[])" != "Module(body=[Expr(value=Constant(value=b'123'))], type_ignores=[])"
- Module(body=[Expr(value=Constant(value=b'123', kind=None))], type_ignores=[])
?                                              -----------
+ Module(body=[Expr(value=Constant(value=b'123'))], type_ignores=[])


======================================================================
FAIL: test_chained_comparisons (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 261, in test_chained_comparisons
    self.check_roundtrip("1 < 4 <= 5")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Modu[44 chars]lue=1, kind=None), ops=[Lt(), LtE()], comparat[81 chars]=[])' != 'Modu[44 chars]lue=1), ops=[Lt(), LtE()], comparators=[Consta[48 chars]=[])'
- Module(body=[Expr(value=Compare(left=Constant(value=1, kind=None), ops=[Lt(), LtE()], comparators=[Constant(value=4, kind=None), Constant(value=5, kind=None)]))], type_ignores=[])
?                                                      -----------                                                   -----------                   -----------
+ Module(body=[Expr(value=Compare(left=Constant(value=1), ops=[Lt(), LtE()], comparators=[Constant(value=4), Constant(value=5)]))], type_ignores=[])


======================================================================
FAIL: test_class_definition (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 374, in test_class_definition
    self.check_roundtrip("class A(metaclass=type, *[], **{}): pass")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[164 chars]word(arg=None, value=Dict(keys=[], values=[]))[51 chars]=[])" != "Modu[164 chars]word(value=Dict(keys=[], values=[]))], body=[P[41 chars]=[])"
Diff is 721 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_complex_f_string (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 327, in test_complex_f_string
    self.check_roundtrip(complex_f_string)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[51 chars]e='-', kind=None), FormattedValue(value=Joined[519 chars]=[])" != "Modu[51 chars]e='-'), FormattedValue(value=JoinedStr(values=[359 chars]=[])"
Diff is 1095 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_dict_comprehension (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 347, in test_dict_comprehension
    self.check_roundtrip("{x: x*x for x in range(10)}")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[269 chars]ue=10, kind=None)], keywords=[]), ifs=[], is_a[25 chars]=[])" != "Modu[269 chars]ue=10)], keywords=[]), ifs=[], is_async=0)]))][14 chars]=[])"
Diff is 1273 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_for_else (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 206, in test_for_else
    self.check_roundtrip(for_else)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[66 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[482 chars]=[])" != "Modu[66 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[334 chars]=[])"
Diff is 1063 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_formatted_value (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 298, in test_formatted_value
    self.check_roundtrip('f"{value}"')
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[81 chars]oad()), conversion=-1, format_spec=None)]))], type_ignores=[])" != "Modu[81 chars]oad()), conversion=-1)]))], type_ignores=[])"
- Module(body=[Expr(value=JoinedStr(values=[FormattedValue(value=Name(id='value', ctx=Load()), conversion=-1, format_spec=None)]))], type_ignores=[])
?                                                                                                           ------------------
+ Module(body=[Expr(value=JoinedStr(values=[FormattedValue(value=Name(id='value', ctx=Load()), conversion=-1)]))], type_ignores=[])


======================================================================
FAIL: test_function_arguments (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 265, in test_function_arguments
    self.check_roundtrip("def f(): pass")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[66 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[110 chars]=[])" != "Modu[66 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[52 chars]=[])"
- Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None)], type_ignores=[])
?                                                                           -------------                                ------------                                              ---------------------------------
+ Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]), body=[Pass()], decorator_list=[])], type_ignores=[])


======================================================================
FAIL: test_huge_float (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 223, in test_huge_float
    self.check_roundtrip("1e1000")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Module(body=[Expr(value=Constant(value=inf, kind=None))], type_ignores=[])' != 'Module(body=[Expr(value=Constant(value=inf))], type_ignores=[])'
- Module(body=[Expr(value=Constant(value=inf, kind=None))], type_ignores=[])
?                                           -----------
+ Module(body=[Expr(value=Constant(value=inf))], type_ignores=[])


======================================================================
FAIL: test_imaginary_literals (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 239, in test_imaginary_literals
    self.check_roundtrip("7j")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Module(body=[Expr(value=Constant(value=7j, kind=None))], type_ignores=[])' != 'Module(body=[Expr(value=Constant(value=7j))], type_ignores=[])'
- Module(body=[Expr(value=Constant(value=7j, kind=None))], type_ignores=[])
?                                          -----------
+ Module(body=[Expr(value=Constant(value=7j))], type_ignores=[])


======================================================================
FAIL: test_import_many (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 283, in test_import_many
    self.check_roundtrip(import_many)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[35 chars]fred', asname=None), alias(name='barney', asna[25 chars]=[])" != "Modu[35 chars]fred'), alias(name='barney')])], type_ignores=[])"
- Module(body=[Import(names=[alias(name='fred', asname=None), alias(name='barney', asname=None)])], type_ignores=[])
?                                             -------------                      -------------
+ Module(body=[Import(names=[alias(name='fred'), alias(name='barney')])], type_ignores=[])


======================================================================
FAIL: test_joined_str (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 305, in test_joined_str
    self.check_roundtrip('f"{key}={value!s}"')
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[95 chars]on=-1, format_spec=None), Constant(value='=', [114 chars]=[])" != "Modu[95 chars]on=-1), Constant(value='='), FormattedValue(va[67 chars]=[])"
- Module(body=[Expr(value=JoinedStr(values=[FormattedValue(value=Name(id='key', ctx=Load()), conversion=-1, format_spec=None), Constant(value='=', kind=None), FormattedValue(value=Name(id='value', ctx=Load()), conversion=115, format_spec=None)]))], type_ignores=[])
+ Module(body=[Expr(value=JoinedStr(values=[FormattedValue(value=Name(id='key', ctx=Load()), conversion=-1), Constant(value='='), FormattedValue(value=Name(id='value', ctx=Load()), conversion=115)]))], type_ignores=[])


======================================================================
FAIL: test_lambda_parentheses (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 258, in test_lambda_parentheses
    self.check_roundtrip("(lambda: int)()")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[72 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[100 chars]=[])" != "Modu[72 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[75 chars]=[])"
- Module(body=[Expr(value=Call(func=Lambda(args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=Name(id='int', ctx=Load())), args=[], keywords=[]))], type_ignores=[])
?                                                                                 -------------                                ------------
+ Module(body=[Expr(value=Call(func=Lambda(args=arguments(posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]), body=Name(id='int', ctx=Load())), args=[], keywords=[]))], type_ignores=[])


======================================================================
FAIL: test_min_int30 (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 235, in test_min_int30
    self.check_roundtrip(str(-2**31))
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Modu[43 chars]and=Constant(value=2147483648, kind=None)))], type_ignores=[])' != 'Modu[43 chars]and=Constant(value=2147483648)))], type_ignores=[])'
- Module(body=[Expr(value=UnaryOp(op=USub(), operand=Constant(value=2147483648, kind=None)))], type_ignores=[])
?                                                                             -----------
+ Module(body=[Expr(value=UnaryOp(op=USub(), operand=Constant(value=2147483648)))], type_ignores=[])


======================================================================
FAIL: test_negative_zero (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 248, in test_negative_zero
    self.check_roundtrip("-0")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Modu[34 chars]b(), operand=Constant(value=0, kind=None)))], type_ignores=[])' != 'Modu[34 chars]b(), operand=Constant(value=0)))], type_ignores=[])'
- Module(body=[Expr(value=UnaryOp(op=USub(), operand=Constant(value=0, kind=None)))], type_ignores=[])
?                                                                    -----------
+ Module(body=[Expr(value=UnaryOp(op=USub(), operand=Constant(value=0)))], type_ignores=[])


======================================================================
FAIL: test_nonlocal (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 287, in test_nonlocal
    self.check_roundtrip(nonlocal_ex)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[66 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[837 chars]=[])" != "Modu[66 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[573 chars]=[])"
Diff is 1657 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_raise_from (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 291, in test_raise_from
    self.check_roundtrip(raise_from)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[52 chars]lue=1, kind=None), op=Div(), right=Constant(va[234 chars]=[])" != "Modu[52 chars]lue=1), op=Div(), right=Constant(value=0)))], [212 chars]=[])"
Diff is 665 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_relative_import (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 280, in test_relative_import
    self.check_roundtrip(relative_import)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[15 chars]From(module=None, names=[alias(name='fred', as[201 chars]=[])" != "Modu[15 chars]From(names=[alias(name='fred')], level=1), Imp[149 chars]=[])"
- Module(body=[ImportFrom(module=None, names=[alias(name='fred', asname=None)], level=1), ImportFrom(module=None, names=[alias(name='barney', asname=None)], level=2), ImportFrom(module='australia', names=[alias(name='shrimp', asname='prawns')], level=1)], type_ignores=[])
+ Module(body=[ImportFrom(names=[alias(name='fred')], level=1), ImportFrom(names=[alias(name='barney')], level=2), ImportFrom(module='australia', names=[alias(name='shrimp', asname='prawns')], level=1)], type_ignores=[])


======================================================================
FAIL: test_set_comprehension (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 343, in test_set_comprehension
    self.check_roundtrip("{x for x in range(5)}")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[180 chars]lue=5, kind=None)], keywords=[]), ifs=[], is_a[25 chars]=[])" != "Modu[180 chars]lue=5)], keywords=[]), ifs=[], is_async=0)]))][14 chars]=[])"
Diff is 917 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_set_literal (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 339, in test_set_literal
    self.check_roundtrip("{'a', 'b', 'c'}")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[43 chars]e='a', kind=None), Constant(value='b', kind=No[53 chars]=[])" != "Modu[43 chars]e='a'), Constant(value='b'), Constant(value='c[20 chars]=[])"
- Module(body=[Expr(value=Set(elts=[Constant(value='a', kind=None), Constant(value='b', kind=None), Constant(value='c', kind=None)]))], type_ignores=[])
?                                                     -----------                     -----------                     -----------
+ Module(body=[Expr(value=Set(elts=[Constant(value='a'), Constant(value='b'), Constant(value='c')]))], type_ignores=[])


======================================================================
FAIL: test_shifts (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 202, in test_shifts
    self.check_roundtrip("45 << 2")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Modu[43 chars]ue=45, kind=None), op=LShift(), right=Constant[37 chars]=[])' != 'Modu[43 chars]ue=45), op=LShift(), right=Constant(value=2)))[15 chars]=[])'
- Module(body=[Expr(value=BinOp(left=Constant(value=45, kind=None), op=LShift(), right=Constant(value=2, kind=None)))], type_ignores=[])
?                                                     -----------                                      -----------
+ Module(body=[Expr(value=BinOp(left=Constant(value=45), op=LShift(), right=Constant(value=2)))], type_ignores=[])


======================================================================
FAIL: test_starred_assignment (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 385, in test_starred_assignment
    self.check_roundtrip("a, *b, c = seq")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[171 chars]e(id='seq', ctx=Load()), type_comment=None)], type_ignores=[])" != "Modu[171 chars]e(id='seq', ctx=Load()))], type_ignores=[])"
Diff is 895 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_try_except_finally (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 381, in test_try_except_finally
    self.check_roundtrip(try_except_finally)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[115 chars]()), name=None, body=[Expr(value=Name(id='suit[252 chars]=[])" != "Modu[115 chars]()), body=[Expr(value=Name(id='suite2', ctx=Lo[230 chars]=[])"
Diff is 827 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_unary_parens (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 212, in test_unary_parens
    self.check_roundtrip("(-1)**7")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: 'Modu[69 chars]lue=1, kind=None)), op=Pow(), right=Constant(v[35 chars]=[])' != 'Modu[69 chars]lue=1)), op=Pow(), right=Constant(value=7)))],[13 chars]=[])'
- Module(body=[Expr(value=BinOp(left=UnaryOp(op=USub(), operand=Constant(value=1, kind=None)), op=Pow(), right=Constant(value=7, kind=None)))], type_ignores=[])
?                                                                               -----------                                    -----------
+ Module(body=[Expr(value=BinOp(left=UnaryOp(op=USub(), operand=Constant(value=1)), op=Pow(), right=Constant(value=7)))], type_ignores=[])


======================================================================
FAIL: test_variable_annotation (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 392, in test_variable_annotation
    self.check_roundtrip("a: int")
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[70 chars]d='int', ctx=Load()), value=None, simple=1)], type_ignores=[])" != "Modu[70 chars]d='int', ctx=Load()), simple=1)], type_ignores=[])"
- Module(body=[AnnAssign(target=Name(id='a', ctx=Store()), annotation=Name(id='int', ctx=Load()), value=None, simple=1)], type_ignores=[])
?                                                                                                 ------------
+ Module(body=[AnnAssign(target=Name(id='a', ctx=Store()), annotation=Name(id='int', ctx=Load()), simple=1)], type_ignores=[])


======================================================================
FAIL: test_while_else (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 209, in test_while_else
    self.check_roundtrip(while_else)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[66 chars]=[], vararg=None, kwonlyargs=[], kw_defaults=[[372 chars]=[])" != "Modu[66 chars]=[], kwonlyargs=[], kw_defaults=[], defaults=[[243 chars]=[])"
Diff is 862 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_with_as (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 406, in test_with_as
    self.check_roundtrip(with_as)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[170 chars]'suite1', ctx=Load()))], type_comment=None)], type_ignores=[])" != "Modu[170 chars]'suite1', ctx=Load()))])], type_ignores=[])"
Diff is 891 characters long. Set self.maxDiff to None to see it.

======================================================================
FAIL: test_with_simple (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 403, in test_with_simple
    self.check_roundtrip(with_simple)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[95 chars]s=[]), optional_vars=None)], body=[Expr(value=[66 chars]=[])" != "Modu[95 chars]s=[]))], body=[Expr(value=Name(id='suite1', ct[27 chars]=[])"
- Module(body=[With(items=[withitem(context_expr=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[]), optional_vars=None)], body=[Expr(value=Name(id='suite1', ctx=Load()))], type_comment=None)], type_ignores=[])
?                                                                                                         --------------------                                                    -------------------
+ Module(body=[With(items=[withitem(context_expr=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[]))], body=[Expr(value=Name(id='suite1', ctx=Load()))])], type_ignores=[])


======================================================================
FAIL: test_with_two_items (tests.test_dump.DumpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../astunparse/tests/common.py", line 410, in test_with_two_items
    self.check_roundtrip(with_two_items)
  File ".../astunparse/tests/test_dump.py", line 24, in check_roundtrip
    self.assertASTEqual(dump1, dump2)
  File ".../astunparse/tests/test_dump.py", line 18, in assertASTEqual
    self.assertEqual(dump1, dump2)
AssertionError: "Modu[293 chars]'suite1', ctx=Load()))], type_comment=None)], type_ignores=[])" != "Modu[293 chars]'suite1', ctx=Load()))])], type_ignores=[])"
Diff is 1377 characters long. Set self.maxDiff to None to see it.

----------------------------------------------------------------------
Ran 94 tests in 0.060s

FAILED (failures=37, skipped=8)
Test failed: <unittest.runner.TextTestResult run=94 errors=0 failures=37>
error: Test failed: <unittest.runner.TextTestResult run=94 errors=0 failures=37>
ERROR: InvocationError for command .../astunparse/.tox/py39/bin/python setup.py test (exited with code 1)
___________________________________ summary ____________________________________
ERROR:   py39: commands failed

missing _Expression and _Interactive

    ########################################################

    def _Module(self, tree):
        for stmt in tree.body:
            self.dispatch(stmt)

    def _Interactive(self, tree):
        for stmt in tree.body:
            self.dispatch(stmt)

    def _Expression(self, tree):
        self.dispatch(tree.body)

    # stmt
    def _Expr(self, tree):
        self.fill()
        self.dispatch(tree.value)

Contributing f-string fixes to CPython

Hello again!

ast.unparse in Python 3.9 currently can't handle the complex f-strings that astunparse can, see https://bugs.python.org/issue28002 as the relevant issue. It might make sense to contribute the changes you made to support this back upstream.
In the bugs.python.org issue I linked, I offered to make a PR to CPython based on your change (and giving you the credit, of course). If you're busy or you don't see this issue, that offer still stands, but the happiest thing might be if you submitted the PR yourself.

Let me know if you have any thoughts!

--dump flag is broken

Initial problem starts here:

    parser.add_argument(
        '--dump',
        type=bool,
        help="Show a pretty-printed AST instead of the source"
    )

It would rather be action="store_true".

Working that around and running as python3 -m astunparse test.py --dump=True, with a trivial test.py of print(1) leads to the exception below. Because a filename is passed to a function which actually expects an AST tree.

$ python3 -m astunparse test.py --dump=True
test.py
=======
Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/__main__.py", line 48, in <module>
    main(sys.argv[1:])
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/__main__.py", line 44, in main
    roundtrip_recursive(target, dump_tree=arguments.dump)
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/__main__.py", line 14, in roundtrip_recursive
    dump(target)
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/__init__.py", line 19, in dump
    Printer(file=v).visit(tree)
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/printer.py", line 16, in visit
    super(Printer, self).visit(node)
  File "/usr/lib/python3.6/ast.py", line 253, in visit
    return visitor(node)
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/printer.py", line 30, in generic_visit
    children = [(name + "=", value) for name, value in ast.iter_fields(node)]
  File "/home/pfalcon/projects-3rdparty/Python/Python-ast/astunparse/lib/astunparse/printer.py", line 30, in <listcomp>
    children = [(name + "=", value) for name, value in ast.iter_fields(node)]
  File "/usr/lib/python3.6/ast.py", line 171, in iter_fields
    for field in node._fields:
AttributeError: 'str' object has no attribute '_fields'

Can't unparse code wiht dict unpacking

When I'm trying to unparse ast with dict unpacking it fails with:

unparse(ast.parse('{1: 1, **x}'))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-d010b38dd24f> in <module>()
----> 1 unparse(ast.parse('{1: 1, **x}'))

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/__init__.py in unparse(tree)
     11 def unparse(tree):
     12     v = cStringIO()
---> 13     Unparser(tree, file=v)
     14     return v.getvalue()
     15 

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in __init__(self, tree, file)
     36         self.future_imports = []
     37         self._indent = 0
---> 38         self.dispatch(tree)
     39         print("", file=self.f)
     40         self.f.flush()

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in dispatch(self, tree)
     64             return
     65         meth = getattr(self, "_"+tree.__class__.__name__)
---> 66         meth(tree)
     67 
     68 

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in _Module(self, tree)
     76     def _Module(self, tree):
     77         for stmt in tree.body:
---> 78             self.dispatch(stmt)
     79 
     80     def _Interactive(self, tree):

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in dispatch(self, tree)
     64             return
     65         meth = getattr(self, "_"+tree.__class__.__name__)
---> 66         meth(tree)
     67 
     68 

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in _Expr(self, tree)
     88     def _Expr(self, tree):
     89         self.fill()
---> 90         self.dispatch(tree.value)
     91 
     92     def _Import(self, t):

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in dispatch(self, tree)
     64             return
     65         meth = getattr(self, "_"+tree.__class__.__name__)
---> 66         meth(tree)
     67 
     68 

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in _Dict(self, t)
    565             self.write(": ")
    566             self.dispatch(v)
--> 567         interleave(lambda: self.write(", "), write_pair, zip(t.keys, t.values))
    568         self.write("}")
    569 

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in interleave(inter, f, seq)
     23         for x in seq:
     24             inter()
---> 25             f(x)
     26 
     27 class Unparser:

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in write_pair(pair)
    562         def write_pair(pair):
    563             (k, v) = pair
--> 564             self.dispatch(k)
    565             self.write(": ")
    566             self.dispatch(v)

/home/nvbn/miniconda3/lib/python3.6/site-packages/astunparse/unparser.py in dispatch(self, tree)
     63                 self.dispatch(t)
     64             return
---> 65         meth = getattr(self, "_"+tree.__class__.__name__)
     66         meth(tree)
     67 

AttributeError: 'Unparser' object has no attribute '_NoneType'

pkg_resources.DistributionNotFound: The 'wheel<1.0,>=0.23.0' distribution was not found and is required by astunparse

Hey @simonpercivall, sorry to come bother you here with that, but maybe you'll have an idea on why it's happening?

Basically, upon running mkdocs serve in a manually created virtualenv (python3.8 -m venv env), we get this error.
mkdocs serve builds the site, the mkdocstrings plugin then calls pytkdocs which depends on astunparse.

INFO    -  Building documentation... 
Traceback (most recent call last):
  File "/Users/an/Desktop/mkdocstrings/env/bin/mkdocs", line 10, in <module>
    sys.exit(cli())
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/__main__.py", line 133, in serve_command
    serve.serve(
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/commands/serve.py", line 141, in serve
    config = builder()
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/commands/serve.py", line 122, in builder
    config = load_config(
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/base.py", line 197, in load_config
    errors, warnings = cfg.validate()
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/base.py", line 107, in validate
    run_failed, run_warnings = self._validate()
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/base.py", line 62, in _validate
    self[key] = config_option.validate(value)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/config_options.py", line 131, in validate
    return self.run_validation(value)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/config_options.py", line 609, in run_validation
    plgins[item] = self.load_plugin(item, cfg)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/mkdocs/config/config_options.py", line 617, in load_plugin
    Plugin = self.installed_plugins[name].load()
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2442, in load
    self.require(*args, **kwargs)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2465, in require
    items = working_set.resolve(reqs, env, installer, extras=self.extras)
  File "/Users/an/Desktop/mkdocstrings/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'wheel<1.0,>=0.23.0' distribution was not found and is required by astunparse

Do you have any idea why pkg_resources is complaning? Well, the obvious explanation is that wheel is not installed, but is there some distribution settings on your side possibly involved?

For reference: mkdocstrings/mkdocstrings#262

Please clarify the actual license

The LICENSE file lists 2 licenses. A BSD-like one and also PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2. Do I choose freely? Are some bits licensed under BSD and other bits under PSF? Which bits?

setup.py has:

    license="BSD",

and

    'License :: OSI Approved :: BSD License',

Thanks for clarifying.

unit test failure python 3.6.5

python setup.py test

======================================================================
ERROR: test_joined_str (tests.test_unparse.UnparseTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/sclarkson/astunparse/tests/common.py", line 296, in test_joined_str
    self.check_roundtrip('f"{key:4}={value!s}"')
  File "/home/sclarkson/astunparse/tests/test_unparse.py", line 19, in check_roundtrip
    ast2 = compile(code2, filename, mode, ast.PyCF_ONLY_AST)
  File "internal", line 2
    f'''{key:f'''4'''}={value!s}'''
                 ^
SyntaxError: invalid syntax

----------------------------------------------------------------------

This is on Arch Linux

python --version
Python 3.6.5

extra parens on every expression

I understand the utility doesn't aim to exactly replicate the original source, but having extra parens on every expression is a fairly distracting artifact when looking at unparse output.

Is astunparse able to deduce when the parens are extraneous?

python 3.6 support

Hello,

does this library support python 3.6? Are you planning on adding support for 3.6 if it doesn't currently?

Thanks much,

William

`if`s are unparsed inconsistently to `ast.unparse`, which breaks tests of the libs using `astunparse.unparse` as a fallback impl.

import ast

import astunparse
import sh  # allows calling binaries as functions
from MempipedPath import MempipedPathRead  # maps byte arrays to anonymous memory maps and exposes them as paths in file system

n = ast.If(
	test=ast.Compare(left=ast.Name(id="a", ctx=ast.Load()), ops=[ast.Gt()], comparators=[ast.Constant(value=0)]),
	body=[ast.Expr(value=ast.Name(id="b", ctx=ast.Load()))],
	orelse=[ast.Expr(value=ast.Name(id="c", ctx=ast.Load()))],
)
ast.fix_missing_locations(n)


if __name__ == "__main__":
	with MempipedPathRead(ast.unparse(n).strip()) as f1:
		with MempipedPathRead(astunparse.unparse(n).strip()) as f2:
			print(sh.diff("-u", f1, "--label", "ast", f2, "--label", "astunparse", _ok_code=1, _env={"LANG": "C"}))

results in

--- ast
+++ astunparse
@@ -1,4 +1,4 @@
-if a > 0:
+if (a > 0):
     b
 else:
     c
\ No newline at end of file

astor.to_source works fine.

incorrect escaped string unparse

create a file with following contents and name it target.py:

phys_vol_name = "test123"
raise RuntimeError(f'Can\'t find the volume group "{phys_vol_name}"')

then use this script to parse & unparse to create new file result.py

import ast
import astunparse

with open("target.py", "r") as f:
    res = astunparse.unparse(ast.parse(f.read()))

with open("result.py", "w") as ff:
    ff.write(res)

when you run result.py you get syntaxt error:

File "result.py", line 3
    raise RuntimeError(f"""Can't find the volume group "{phys_vol_name}"""")
                                                                           ^
SyntaxError: EOL while scanning string literal

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.