Repository: google/yapf
Pull request #352 (2016-12-29 17:08:38)

Add handling for unpacking with dict expressions (currently 3.6 only)
Only works on Python 3.6, since lib2to3 in Python 3.5 doesn't
recognize the unpacking token.

Prior to this patch,
```{**x}```
would raise an error like this in Python 3.5: 
```lib2to3.pgen2.parse.ParseError: bad input: type=36, value='**', context=('   ', (1, 11))```.

This is a known issue.

In Python 3.6, this is apparently fixed, so that we can indeed support PEP448 cases.



googlebot: We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s).  If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA ([login here](https://cla.developers.google.com/) to double check)?  If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.

<!-- need_author_cla -->
coveralls: 
[![Coverage Status](https://coveralls.io/builds/9461429/badge)](https://coveralls.io/builds/9461429)

Coverage decreased (-0.04%) to 96.272% when pulling **99a8506f318e0b0e3621c578718216ef99fa2b11 on bastih:doublestar-in-dictset** into **21946cb522469265b2629bf13933d92cc73f43c8 on google:master**.


Patch:
diff --git a/.travis.yml b/.travis.yml
index 87965b4..1d90518 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@ python:
   - 2.7
   - 3.4
   - 3.5
+  - 3.6
   - nightly
 
 matrix:
diff --git a/tox.ini b/tox.ini
index 3109915..dca30d2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=py27,py34,py35
+envlist=py27,py34,py35,py36
 
 [testenv]
 commands=
diff --git a/yapf/yapflib/py3compat.py b/yapf/yapflib/py3compat.py
index 81d4847..78047cb 100644
--- a/yapf/yapflib/py3compat.py
+++ b/yapf/yapflib/py3compat.py
@@ -17,6 +17,7 @@ import io
 import sys
 
 PY3 = sys.version_info[0] == 3
+PY36 = sys.version_info[0] == 3 and sys.version_info[1] >= 6
 
 if PY3:
   StringIO = io.StringIO
diff --git a/yapf/yapflib/subtype_assigner.py b/yapf/yapflib/subtype_assigner.py
index b978368..48a9bed 100644
--- a/yapf/yapflib/subtype_assigner.py
+++ b/yapf/yapflib/subtype_assigner.py
@@ -75,13 +75,15 @@ class _SubtypeAssigner(pytree_visitor.PyTreeVisitor):
         comp_for = True
         _AppendFirstLeafTokenSubtype(child,
                                      format_token.Subtype.DICT_SET_GENERATOR)
-      elif pytree_utils.NodeName(child) == 'COLON':
+      elif pytree_utils.NodeName(child) in ('COLON', 'DOUBLESTAR'):
         dict_maker = True
 
     if not comp_for and dict_maker:
       last_was_colon = False
       for child in node.children:
         if dict_maker:
+          if pytree_utils.NodeName(child) == 'DOUBLESTAR':
+            _AppendFirstLeafTokenSubtype(child, format_token.Subtype.KWARGS_STAR_STAR)
           if last_was_colon:
             if style.Get('INDENT_DICTIONARY_VALUE'):
               _InsertPseudoParentheses(child)


Test patch:
diff --git a/yapftests/reformatter_python3_test.py b/yapftests/reformatter_python3_test.py
index c5a97e6..9ddf4dc 100644
--- a/yapftests/reformatter_python3_test.py
+++ b/yapftests/reformatter_python3_test.py
@@ -59,6 +59,23 @@ class TestsForPython3Code(yapf_test_helper.YAPFTest):
     uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
     self.assertCodeEqual(expected_formatted_code, reformatter.Reformat(uwlines))
 
+  @unittest.skipUnless(py3compat.PY36, 'Requires Python 3.6')
+  def testPEP448ParameterExpansion(self):
+    unformatted_code = textwrap.dedent("""\
+    { ** x }
+    {   **{}   }
+    { **{   **x },  **x }
+    {'a': 1,   **kw , 'b':3,  **kw2   }
+    """)
+    expected_formatted_code = textwrap.dedent("""\
+    {**x}
+    {**{}}
+    {**{**x}, **x}
+    {'a': 1, **kw, 'b': 3, **kw2}
+    """)
+    uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
+    self.assertCodeEqual(expected_formatted_code, reformatter.Reformat(uwlines))
+
   def testAnnotations(self):
     unformatted_code = textwrap.dedent("""\
         def foo(a: list, b: "bar") -> dict: