diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index e2f87524c218b6..c3c772feb5c0ad 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -380,55 +380,6 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
#endif
-/* Define a pair of assertion macros:
- _PyObject_ASSERT_FROM(), _PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT().
-
- These work like the regular C assert(), in that they will abort the
- process with a message on stderr if the given condition fails to hold,
- but compile away to nothing if NDEBUG is defined.
-
- However, before aborting, Python will also try to call _PyObject_Dump() on
- the given object. This may be of use when investigating bugs in which a
- particular object is corrupt (e.g. buggy a tp_visit method in an extension
- module breaking the garbage collector), to help locate the broken objects.
-
- The WITH_MSG variant allows you to supply an additional message that Python
- will attempt to print to stderr, after the object dump. */
-#ifdef NDEBUG
- /* No debugging: compile away the assertions: */
-# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \
- ((void)0)
-#else
- /* With debugging: generate checks: */
-# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \
- ((expr) \
- ? (void)(0) \
- : _PyObject_AssertFailed((obj), Py_STRINGIFY(expr), \
- (msg), (filename), (lineno), (func)))
-#endif
-
-#define _PyObject_ASSERT_WITH_MSG(obj, expr, msg) \
- _PyObject_ASSERT_FROM((obj), expr, (msg), __FILE__, __LINE__, __func__)
-#define _PyObject_ASSERT(obj, expr) \
- _PyObject_ASSERT_WITH_MSG((obj), expr, NULL)
-
-#define _PyObject_ASSERT_FAILED_MSG(obj, msg) \
- _PyObject_AssertFailed((obj), NULL, (msg), __FILE__, __LINE__, __func__)
-
-/* Declare and define _PyObject_AssertFailed() even when NDEBUG is defined,
- to avoid causing compiler/linker errors when building extensions without
- NDEBUG against a Python built with NDEBUG defined.
-
- msg, expr and function can be NULL. */
-PyAPI_FUNC(void) _Py_NO_RETURN _PyObject_AssertFailed(
- PyObject *obj,
- const char *expr,
- const char *msg,
- const char *file,
- int line,
- const char *function);
-
-
PyAPI_FUNC(void) _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op);
PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(PyThreadState *tstate);
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index a6519aa086309d..8de8344637a1c6 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -11,6 +11,7 @@ extern "C" {
#include "pycore_interp_structs.h" // PyGC_Head
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_typedefs.h" // _PyInterpreterFrame
+#include "pycore_utils.h" // _PyObject_ASSERT_FROM()
/* Get an object's GC head */
diff --git a/Include/internal/pycore_utils.h b/Include/internal/pycore_utils.h
new file mode 100644
index 00000000000000..3b12d26b20ca80
--- /dev/null
+++ b/Include/internal/pycore_utils.h
@@ -0,0 +1,62 @@
+#ifndef Py_INTERNAL_UTILS_H
+#define Py_INTERNAL_UTILS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+/* Define a pair of assertion macros:
+ * _PyObject_ASSERT_FROM(), _PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT().
+ *
+ * These work like the regular C assert(), in that they will abort the
+ * process with a message on stderr if the given condition fails to hold,
+ * but compile away to nothing if NDEBUG is defined.
+ *
+ * However, before aborting, Python will also try to call _PyObject_Dump() on
+ * the given object. This may be of use when investigating bugs in which a
+ * particular object is corrupt (e.g. buggy a tp_visit method in an extension
+ * module breaking the garbage collector), to help locate the broken objects.
+ *
+ * The WITH_MSG variant allows you to supply an additional message that Python
+ * will attempt to print to stderr, after the object dump. */
+#ifdef NDEBUG
+ /* No debugging: compile away the assertions: */
+# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \
+ ((void)0)
+#else
+ /* With debugging: generate checks: */
+# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \
+ ((expr) \
+ ? (void)(0) \
+ : _PyObject_AssertFailed((obj), Py_STRINGIFY(expr), \
+ (msg), (filename), (lineno), (func)))
+#endif
+
+#define _PyObject_ASSERT_WITH_MSG(obj, expr, msg) \
+ _PyObject_ASSERT_FROM((obj), expr, (msg), __FILE__, __LINE__, __func__)
+#define _PyObject_ASSERT(obj, expr) \
+ _PyObject_ASSERT_WITH_MSG((obj), expr, NULL)
+
+#define _PyObject_ASSERT_FAILED_MSG(obj, msg) \
+ _PyObject_AssertFailed((obj), NULL, (msg), __FILE__, __LINE__, __func__)
+
+/* Declare and define _PyObject_AssertFailed() even when NDEBUG is defined,
+ to avoid causing compiler/linker errors when building extensions without
+ NDEBUG against a Python built with NDEBUG defined.
+
+ msg, expr and function can be NULL. */
+PyAPI_FUNC(void) _Py_NO_RETURN _PyObject_AssertFailed(
+ PyObject *obj,
+ const char *expr,
+ const char *msg,
+ const char *file,
+ int line,
+ const char *function);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_UTILS_H */
diff --git a/Makefile.pre.in b/Makefile.pre.in
index a5223246845dcf..0d93e8903bf9a2 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1439,6 +1439,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_uop.h \
$(srcdir)/Include/internal/pycore_uop_ids.h \
$(srcdir)/Include/internal/pycore_uop_metadata.h \
+ $(srcdir)/Include/internal/pycore_utils.h \
$(srcdir)/Include/internal/pycore_warnings.h \
$(srcdir)/Include/internal/pycore_weakref.h \
$(DTRACE_HEADERS) \
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index e2e1e415827e6f..f1ff3de3383e36 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -331,6 +331,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 7e7ed9c2ae6c43..6ac35c01b08fa6 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -534,6 +534,9 @@
Include\internal
+
+ Include\internal
+
Include\internal