Repository: berkerpeksag/astor

Issue:
parenthesis around expressions in a JoinedString
Astor wraps expressions into parenthesis, also when this is completely redundant:

```
code = '''print(f"{var + 1:.2f}")'''

import ast
tree = ast.parse(code)
import astor
recovered = astor.to_source(tree)

print('original:   ',code)
print('astor:      ',recovered)
```

output:

```
original:    print(f"{var + 1:.2f}")
astor:       print(f'{(var + 1):.2f}')
```

Python: 3.6.8
astor: 0.8.0



Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 16f9cb1..c61f980 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,18 +5,15 @@ python:
   - 3.4
   - 3.5
   - 3.6
+  - 3.7
+  - 3.8
+  - 3.9-dev
   - pypy
   - pypy3.5
 matrix:
-  include:
-  # See https://github.com/travis-ci/travis-ci/issues/9069
-  # for more information.
-  - python: 3.7
-    sudo: required
-    dist: xenial
-  - python: 3.8-dev
-    sudo: required
-    dist: xenial
+  fast_finish: true
+  allow_failures:
+    - python: 3.9-dev
 cache: pip
 install:
   - pip install tox-travis
diff --git a/MANIFEST.in b/MANIFEST.in
index b12a6fe..81e72d5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,4 @@
 include README.rst AUTHORS LICENSE CHANGES
 include setuputils.py
+include astor/VERSION
 recursive-include tests *.py
diff --git a/astor/VERSION b/astor/VERSION
new file mode 100644
index 0000000..a3df0a6
--- /dev/null
+++ b/astor/VERSION
@@ -0,0 +1,1 @@
+0.8.0
diff --git a/astor/__init__.py b/astor/__init__.py
index 3b02983..bdedaef 100644
--- a/astor/__init__.py
+++ b/astor/__init__.py
@@ -9,6 +9,7 @@ Copyright 2013 (c) Berker Peksag
 
 """
 
+import os
 import warnings
 
 from .code_gen import SourceGenerator, to_source  # NOQA
@@ -19,7 +20,9 @@ from .op_util import get_op_symbol, get_op_precedence  # NOQA
 from .op_util import symbol_data  # NOQA
 from .tree_walk import TreeWalk  # NOQA
 
-__version__ = '0.8.0'
+ROOT = os.path.dirname(__file__)
+with open(os.path.join(ROOT, 'VERSION')) as version_file:
+    __version__ = version_file.read().strip()
 
 parse_file = code_to_ast.parse_file
 
diff --git a/astor/code_gen.py b/astor/code_gen.py
index 0d8bd6b..4a634cf 100644
--- a/astor/code_gen.py
+++ b/astor/code_gen.py
@@ -604,6 +604,7 @@ class SourceGenerator(ExplicitNodeVisitor):
                             if getattr(value, 'expr_text', None):
                                 self.write(value.expr_text)
                             else:
+                                set_precedence(value, value.value)
                                 self.visit(value.value)
                             if value.conversion != -1:
                                 self.write('!%s' % chr(value.conversion))
diff --git a/astor/op_util.py b/astor/op_util.py
index e546528..e54e88b 100644
--- a/astor/op_util.py
+++ b/astor/op_util.py
@@ -34,6 +34,7 @@ op_data = """
         ExtSlice                1
     comprehension_target        1
            Tuple                0
+  FormattedValue                0
 
            Comma                1
        NamedExpr                1
diff --git a/docs/changelog.rst b/docs/changelog.rst
index f7d0e6a..d7046e7 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -2,6 +2,19 @@
 Release Notes
 =============
 
+0.8.1 - in development
+----------------------
+
+Bug fixes
+~~~~~~~~~
+
+* Fixed precedence issue for f-string expressions that caused
+  redundant parenthesis around expression.
+  (Reported by Ilya Kamenshchikov in `Issue 153`_ and fixed by Batuhan Taskaya in `PR 155`_.)
+
+.. _`Issue 153`: https://github.com/berkerpeksag/astor/issues/153
+.. _`PR 155`: https://github.com/berkerpeksag/astor/pull/155
+
 0.8.0 - 2019-05-19
 ------------------
 
diff --git a/setup.cfg b/setup.cfg
index 1baf6fc..a43634f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,6 +2,7 @@
 name = astor
 description = Read/rewrite/write Python ASTs
 long_description = file:README.rst
+version = file: astor/VERSION
 author = Patrick Maupin
 author_email = pmaupin@gmail.com
 platforms = Independent
@@ -40,7 +41,7 @@ test_suite = nose.collector
 [options.packages.find]
 exclude = tests
 
-[wheel]
+[bdist_wheel]
 universal = 1
 
 [build-system]
diff --git a/setup.py b/setup.py
index 4a111b5..6068493 100644
--- a/setup.py
+++ b/setup.py
@@ -1,17 +1,3 @@
-import os
-import sys
-
 from setuptools import setup
-from setuptools.config import read_configuration
-
-from setuputils import find_version
-
-
-def here(*paths):
-    return os.path.join(os.path.dirname(__file__), *paths)
-
-config = read_configuration(here('setup.cfg'))
-config['metadata']['version'] = find_version(here('astor', '__init__.py'))
-config['options'].update(config['metadata'])
 
-setup(**config['options'])
+setup()


Test patch:
diff --git a/tests/test_code_gen.py b/tests/test_code_gen.py
index 3cb7e64..45df8c0 100644
--- a/tests/test_code_gen.py
+++ b/tests/test_code_gen.py
@@ -171,9 +171,11 @@ class CodegenTestCase(unittest.TestCase, Comparisons):
         def test(a, b, /, c, *, d, **kwargs):
             pass
 
+
         def test(a=3, b=4, /, c=7):
             pass
 
+
         def test(a, b=4, /, c=8, d=9):
             pass
         """
@@ -456,6 +458,7 @@ class CodegenTestCase(unittest.TestCase, Comparisons):
         x = f'{int(x)}'
         x = f'a{b:c}d'
         x = f'a{b!s:c{d}e}f'
+        x = f'{x + y}'
         x = f'""'
         x = f'"\\''
         """