diff --git a/Include/ast.h b/Include/ast.h
index 434ee18dd91b2a..2f19b1a870dac1 100644
--- a/Include/ast.h
+++ b/Include/ast.h
@@ -9,14 +9,6 @@ extern "C" {
PyAPI_FUNC(int) PyAST_Validate(mod_ty);
-/* _PyAST_ExprAsUnicode is defined in ast_unparse.c */
-PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(expr_ty);
-
-/* Return the borrowed reference to the first literal string in the
- sequence of statements or NULL if it doesn't start from a literal string.
- Doesn't set exception. */
-PyAPI_FUNC(PyObject *) _PyAST_GetDocString(asdl_stmt_seq *);
-
#ifdef __cplusplus
}
#endif
diff --git a/Include/internal/pycore_ast.h b/Include/internal/pycore_ast.h
new file mode 100644
index 00000000000000..38c9212b07ca00
--- /dev/null
+++ b/Include/internal/pycore_ast.h
@@ -0,0 +1,25 @@
+#ifndef Py_INTERNAL_AST_H
+#define Py_INTERNAL_AST_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include "Python-ast.h" // expr_ty
+
+/* _PyAST_ExprAsUnicode is defined in ast_unparse.c */
+extern PyObject* _PyAST_ExprAsUnicode(expr_ty);
+
+/* Return the borrowed reference to the first literal string in the
+ sequence of statements or NULL if it doesn't start from a literal string.
+ Doesn't set exception. */
+extern PyObject* _PyAST_GetDocString(asdl_stmt_seq *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_AST_H */
+
diff --git a/Makefile.pre.in b/Makefile.pre.in
index ee93ab6ca150ea..5ad304e30d0229 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1135,6 +1135,7 @@ PYTHON_HEADERS= \
\
$(srcdir)/Include/internal/pycore_abstract.h \
$(srcdir)/Include/internal/pycore_accu.h \
+ $(srcdir)/Include/internal/pycore_ast.h \
$(srcdir)/Include/internal/pycore_ast_state.h \
$(srcdir)/Include/internal/pycore_atomic.h \
$(srcdir)/Include/internal/pycore_atomic_funcs.h \
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 655c831ed5ff6e..37c60af4f74d7e 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -175,6 +175,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index fba4af536fec90..76894079890fd7 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -486,6 +486,9 @@
Include\internal
+
+ Include\internal
+
Include\internal
diff --git a/Python/ast_opt.c b/Python/ast_opt.c
index 6ad00dcf93cbdf..c76b428f97298d 100644
--- a/Python/ast_opt.c
+++ b/Python/ast_opt.c
@@ -1,7 +1,6 @@
/* AST Optimizer */
#include "Python.h"
-#include "Python-ast.h"
-#include "ast.h"
+#include "pycore_ast.h" // _PyAST_GetDocString()
static int
diff --git a/Python/compile.c b/Python/compile.c
index a1260aadd62e42..6ef5d5f69896c5 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -22,16 +22,15 @@
*/
#include "Python.h"
+#include "pycore_ast.h" // _PyAST_GetDocString()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_long.h" // _PyLong_GetZero()
-#include "Python-ast.h"
-#include "ast.h"
-#include "code.h"
-#include "symtable.h"
+#include "symtable.h" // struct symtable
#define NEED_OPCODE_JUMP_TABLES
-#include "opcode.h"
-#include "wordcode_helpers.h"
+#include "opcode.h" // EXTENDED_ARG
+#include "wordcode_helpers.h" // instrsize()
+
#define DEFAULT_BLOCK_SIZE 16
#define DEFAULT_BLOCKS 8
diff --git a/Python/future.c b/Python/future.c
index 4b73eb64129052..37e7a22f81c2bb 100644
--- a/Python/future.c
+++ b/Python/future.c
@@ -1,9 +1,5 @@
#include "Python.h"
-#include "Python-ast.h"
-#include "token.h"
-#include "code.h"
-#include "symtable.h"
-#include "ast.h"
+#include "pycore_ast.h" // _PyAST_GetDocString()
#define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined"
#define ERR_LATE_FUTURE \