Kodiologist: Okay, I've rebased this one, too.
Kodiologist: I've added more tests from the PEP. I've also used `assertAstRoundtrips` in place of `assertSrcRoundtrips` so the tests are neutral to whether astor produces extra parentheses.
diff --git a/astor/code_gen.py b/astor/code_gen.py
index 9b9c1ea..d79484b 100644
--- a/astor/code_gen.py
+++ b/astor/code_gen.py
@@ -741,6 +741,22 @@ class SourceGenerator(ExplicitNodeVisitor):
             for op, right in zip(node.ops, node.comparators):
                 self.write(get_op_symbol(op, ' %s '), right)
 
+    # assignment expressions; new for Python 3.8
+    def visit_NamedExpr(self, node):
+        with self.delimit(node) as delimiters:
+            p = delimiters.p
+            set_precedence(p, node.target)
+            set_precedence(p + 1, node.value)
+            # Python is picky about delimiters for assignment
+            # expressions: it requires at least one pair in any
+            # statement that uses an assignment expression, even
+            # when not necessary according to the precedence
+            # rules. We address this with the kludge of forcing a
+            # pair of parentheses around every assignment
+            # expression.
+            delimiters.discard = False
+            self.write(node.target, ' := ', node.value)
+
     def visit_UnaryOp(self, node):
         with self.delimit(node, node.op) as delimiters:
             set_precedence(delimiters.p, node.operand)
diff --git a/astor/op_util.py b/astor/op_util.py
index 129b1c5..e546528 100644
--- a/astor/op_util.py
+++ b/astor/op_util.py
@@ -36,6 +36,7 @@ op_data = """
            Tuple                0
 
            Comma                1
+       NamedExpr                1
           Assert                0
            Raise                0
     call_one_arg                1
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 08200f4..684bba4 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -15,6 +15,12 @@ New features
 .. _`Issue 120`: https://github.com/berkerpeksag/astor/issues/120
 .. _`PR 121`: https://github.com/berkerpeksag/astor/pull/121
 
+* Support Python 3.8's assignment expressions.
+  (Reported and fixed by Kodi Arfer in `Issue 126`_ and `PR 134`_.)
+
+.. _`Issue 126`: https://github.com/berkerpeksag/astor/issues/126
+.. _`PR 134`: https://github.com/berkerpeksag/astor/pull/134
+
 Bug fixes
 ~~~~~~~~~
 

Support Python 3.8 assignment expressions in code_gen
    python3 -c 'import ast, astor; print(astor.code_gen.to_source(ast.parse("(a := b)", mode="eval")))'
    …
    AttributeError: No defined handler for node of type NamedExpr

See [PEP 574](https://www.python.org/dev/peps/pep-0572).
diff --git a/tests/test_code_gen.py b/tests/test_code_gen.py
index 05e57fd..c668938 100644
--- a/tests/test_code_gen.py
+++ b/tests/test_code_gen.py
@@ -454,6 +454,27 @@ class CodegenTestCase(unittest.TestCase, Comparisons):
         """
         self.assertSrcRoundtripsGtVer(source, (3, 6))
 
+    def test_assignment_expr(self):
+        cases = (
+            "(x := 3)",
+            "1 + (x := y)",
+            "x = (y := 0)",
+            "1 + (p := 1 if 2 else 3)",
+            "[y := f(x), y**2, y**3]",
+            "(2 ** 3 * 4 + 5 and 6, x := 2 ** 3 * 4 + 5 and 6)",
+            "foo(x := 3, cat='vector')",
+            "foo(x=(y := f(x)))",
+            "any(len(longline := line) >= 100 for line in lines)",
+            "[[y := f(x), x/y] for x in range(5)]",
+            "lambda: (x := 1)",
+            "def foo(answer=(p := 42)): pass",
+            "def foo(answer: (p := 42) = 5): pass",
+            "if reductor := dispatch_table.get(cls): pass",
+            "while line := fp.readline(): pass",
+            "while (command := input('> ')) != 'quit': pass")
+        for case in cases:
+            self.assertAstRoundtripsGtVer(case, (3, 8))
+
     @unittest.skipUnless(sys.version_info <= (3, 3),
                          "ast.Name used for True, False, None until Python 3.4")
     def test_deprecated_constants_as_name(self):

pip install -e .
parse_log_pytest
pytest
nose pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/berkerpeksag/astor.git@262250578230436b288995b3ddf431209633573b#egg=astor
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
nose==1.3.7
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
pytest @ file:///croot/pytest_1738938843180/work
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work

name: astor
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - nose==1.3.7
prefix: /opt/conda/envs/astor

tests/test_code_gen.py::CodegenTestCase::test_assignment_expr
tests/test_code_gen.py::CodegenTestCase::test_huge_int
tests/test_code_gen.py::CodegenTestCase::test_annassign tests/test_code_gen.py::CodegenTestCase::test_arguments tests/test_code_gen.py::CodegenTestCase::test_async_comprehension tests/test_code_gen.py::CodegenTestCase::test_async_def_with_for tests/test_code_gen.py::CodegenTestCase::test_attribute tests/test_code_gen.py::CodegenTestCase::test_class_definition_with_starbases_and_kwargs tests/test_code_gen.py::CodegenTestCase::test_compile_types tests/test_code_gen.py::CodegenTestCase::test_complex tests/test_code_gen.py::CodegenTestCase::test_comprehension tests/test_code_gen.py::CodegenTestCase::test_constant_nodes tests/test_code_gen.py::CodegenTestCase::test_del_statement tests/test_code_gen.py::CodegenTestCase::test_deprecated_constant_nodes tests/test_code_gen.py::CodegenTestCase::test_deprecated_name_constants tests/test_code_gen.py::CodegenTestCase::test_dictionary_literals tests/test_code_gen.py::CodegenTestCase::test_docstring_class tests/test_code_gen.py::CodegenTestCase::test_docstring_function tests/test_code_gen.py::CodegenTestCase::test_docstring_method tests/test_code_gen.py::CodegenTestCase::test_docstring_module tests/test_code_gen.py::CodegenTestCase::test_double_await tests/test_code_gen.py::CodegenTestCase::test_elif tests/test_code_gen.py::CodegenTestCase::test_empty_iterable_literals tests/test_code_gen.py::CodegenTestCase::test_fstring_escaped_braces tests/test_code_gen.py::CodegenTestCase::test_fstring_trailing_newline tests/test_code_gen.py::CodegenTestCase::test_fstrings tests/test_code_gen.py::CodegenTestCase::test_imports tests/test_code_gen.py::CodegenTestCase::test_inf tests/test_code_gen.py::CodegenTestCase::test_matrix_multiplication tests/test_code_gen.py::CodegenTestCase::test_multiple_call_unpackings tests/test_code_gen.py::CodegenTestCase::test_nan tests/test_code_gen.py::CodegenTestCase::test_non_string_leakage tests/test_code_gen.py::CodegenTestCase::test_output_formatting tests/test_code_gen.py::CodegenTestCase::test_pass_arguments_node tests/test_code_gen.py::CodegenTestCase::test_pow tests/test_code_gen.py::CodegenTestCase::test_right_hand_side_dictionary_unpacking tests/test_code_gen.py::CodegenTestCase::test_slicing tests/test_code_gen.py::CodegenTestCase::test_try_expect tests/test_code_gen.py::CodegenTestCase::test_tuple_corner_cases tests/test_code_gen.py::CodegenTestCase::test_unary tests/test_code_gen.py::CodegenTestCase::test_unicode_literals tests/test_code_gen.py::CodegenTestCase::test_with tests/test_code_gen.py::CodegenTestCase::test_yield
astor/code_gen.py docs/changelog.rst astor/op_util.py
astor/code_gen.py docs/changelog.rst astor/op_util.py