WIP.
diff --git a/bazel/workspace_defs.bzl b/bazel/workspace_defs.bzl
index 86324dd..2f596f0 100644
--- a/bazel/workspace_defs.bzl
+++ b/bazel/workspace_defs.bzl
@@ -52,10 +52,6 @@
 )
 """
 
-_build_defs_file = """
-EXT_SUFFIX = "%s"
-"""
-
 def _get_config_var(repository_ctx, name):
   py_program = "import sysconfig; print(sysconfig.get_config_var('%s'), end='')"
   result = repository_ctx.execute(["python3", "-c", py_program % (name)])
@@ -65,11 +61,9 @@
 
 def _python_headers_impl(repository_ctx):
   path = _get_config_var(repository_ctx, "INCLUDEPY")
-  ext_suffix = _get_config_var(repository_ctx, "EXT_SUFFIX")
   repository_ctx.symlink(path, "python")
   python3 = repository_ctx.which("python3")
   repository_ctx.file("BUILD.bazel", _build_file % python3)
-  repository_ctx.file("build_defs.bzl", _build_defs_file % ext_suffix)
 
 # The system_python() repository rule exposes Python headers from the system.
 #
diff --git a/python/BUILD b/python/BUILD
index 102e9f0..68cd74a 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -27,10 +27,6 @@
     "//bazel:build_defs.bzl",
     "UPB_DEFAULT_COPTS",
 )
-load(
-    "@system_python//:build_defs.bzl",
-    "EXT_SUFFIX",
-)
 
 cc_binary(
     name = "message",
@@ -74,7 +70,22 @@
     ],
 )
 
-# Copy the extension into the location recognized by Python.
+cc_binary(
+    name = "api_implementation",
+    srcs = [
+        "api_implementation.c",
+    ],
+    # Enable once linker script is available.
+    #copts = ["-fvisibility=hidden"],
+    deps = ["@system_python//:python_headers"],
+    linkshared = True,
+    linkstatic = True,
+)
+
+# Copy the extensions into the location recognized by Python.
+# .abi3.so indicates use of the limited API, and cross-version ABI compatibility.
+EXT_SUFFIX = ".abi3.so"
+
 genrule(
     name = "message_ext",
     srcs = [":message"],
@@ -89,3 +100,43 @@
     imports = ["."],
     legacy_create_init = False,
 )
+
+TESTS = [
+    "descriptor_database_test",
+    "descriptor_pool_test",
+    "descriptor_test",
+    "generator_test",
+    "json_format_test",
+    "keywords_test",
+    "message_factory_test",
+    "message_test",
+    "proto_builder_test",
+    "reflection_test",
+    "service_reflection_test",
+    "symbol_database_test",
+    "text_encoding_test",
+    "text_format_test",
+    "unknown_fields_test",
+    "well_known_types_test",
+    "wire_format_test",
+]
+
+[
+  py_test(
+      name = test,
+      main = "python/google/protobuf/internal/" + test + ".py",
+      srcs = [
+          "@com_google_protobuf//:python_tests",
+      ],
+      deps = [
+          "@com_google_protobuf//:python_tests",
+      ],
+      data = [
+          "@com_google_protobuf//:python_tests",
+          ":message_ext",
+          ":api_implementation_ext",
+      ],
+      imports = ["."],
+      legacy_create_init = False,
+  ) for test in TESTS
+]
diff --git a/python/minimal_test.py b/python/minimal_test.py
index 072250a..13f1124 100644
--- a/python/minimal_test.py
+++ b/python/minimal_test.py
@@ -50,6 +50,8 @@
         # system.
         self.assertTrue(_message._IS_UPB)
 
+TestMessageExtension.test_descriptor_pool.__unittest_expecting_failure__ = True
+
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(verbosity=2)