feat(pypi): add py.typed files to pyi_srcs (#4202)

Wheel libraries automatically include all their .pyi files in
`pyi_srcs`,
but py.typed files are just as important.  In this patch we begin
including them and make the same change to the runfiles py_library
as well.
diff --git a/news/py-typed-pyi-srcs.added.md b/news/py-typed-pyi-srcs.added.md
new file mode 100644
index 0000000..38447d9
--- /dev/null
+++ b/news/py-typed-pyi-srcs.added.md
@@ -0,0 +1,3 @@
+(pypi) `py.typed` files from wheels are now included in the `pyi_srcs` of the
+wheel's `py_library`, alongside `.pyi` files. The `py.typed` file of
+`//python/runfiles` is now included in its `pyi_srcs` as well.
diff --git a/python/private/pypi/whl_library_srcs.bzl b/python/private/pypi/whl_library_srcs.bzl
index 128c46e..0556219 100644
--- a/python/private/pypi/whl_library_srcs.bzl
+++ b/python/private/pypi/whl_library_srcs.bzl
@@ -212,7 +212,7 @@
         )
 
         pyi_srcs = native.glob(
-            ["site-packages/**/*.pyi"],
+            ["site-packages/**/*.pyi", "site-packages/**/py.typed"],
             allow_empty = True,
         )
 
diff --git a/python/runfiles/BUILD.bazel b/python/runfiles/BUILD.bazel
index 0c9e74a..709bbd3 100644
--- a/python/runfiles/BUILD.bazel
+++ b/python/runfiles/BUILD.bazel
@@ -41,6 +41,7 @@
         # to the --experimental_python_import_all_repositories setting.
         "../..",
     ],
+    pyi_srcs = [":py_typed"],
     visibility = ["//visibility:public"],
 )
 
diff --git a/tests/pypi/whl_library_srcs/whl_library_srcs_tests.bzl b/tests/pypi/whl_library_srcs/whl_library_srcs_tests.bzl
index 1499170..fe4342b 100644
--- a/tests/pypi/whl_library_srcs/whl_library_srcs_tests.bzl
+++ b/tests/pypi/whl_library_srcs/whl_library_srcs_tests.bzl
@@ -145,6 +145,49 @@
 
 _tests.append(_test_sdist_excludes_record)
 
+def _test_pyi_srcs_includes_py_typed(env):
+    py_library_calls = []
+    m_glob = mocks.glob()
+    m_glob.results.append([])  # bin
+    m_glob.results.append([])  # rewrite-bin
+    m_glob.results.append([])  # rewrite-record
+    m_glob.results.append([])  # srcs
+    m_glob.results.append([])  # data
+    m_glob.results.append([
+        "site-packages/foo/__init__.pyi",
+        "site-packages/foo/py.typed",
+    ])  # pyi
+
+    whl_library_srcs(
+        name = "foo.whl",
+        filegroups = {},
+        native = struct(
+            filegroup = lambda **_: None,
+            config_setting = lambda **_: None,
+            glob = m_glob.glob,
+        ),
+        rules = struct(
+            py_library = lambda **kwargs: py_library_calls.append(kwargs),
+            create_inits = lambda **kwargs: [],
+            venv_rewrite_shebang = lambda **kwargs: None,
+            gen_wheel_record = lambda **kwargs: None,
+        ),
+    )
+
+    env.expect.that_collection(m_glob.calls).contains_at_least([
+        mocks.glob_call(
+            ["site-packages/**/*.pyi", "site-packages/**/py.typed"],
+            allow_empty = True,
+        ),
+    ])
+    env.expect.that_int(len(py_library_calls)).equals(1)
+    env.expect.that_collection(py_library_calls[0]["pyi_srcs"]).contains_exactly([
+        "site-packages/foo/__init__.pyi",
+        "site-packages/foo/py.typed",
+    ])
+
+_tests.append(_test_pyi_srcs_includes_py_typed)
+
 def _test_exclude_bazel_files(env):
     # Regression test: the `extracted_whl_files` glob must always exclude the
     # Bazel repo files, even when the wheel is not built from an sdist.