|
16 | 16 |
|
17 | 17 | load("@bazel_skylib//lib:types.bzl", "types") |
18 | 18 | load("//python/private:repo_utils.bzl", "repo_utils") |
| 19 | +load("//python/private:util.bzl", "is_importable_name") |
19 | 20 |
|
20 | 21 | def _get_python_interpreter_attr(mrctx, *, python_interpreter = None): |
21 | 22 | """A helper function for getting the `python_interpreter` attribute or it's default |
@@ -161,9 +162,44 @@ def _execute_checked_stdout(mrctx, *, python, srcs, **kwargs): |
161 | 162 | **_execute_prep(mrctx, python = python, srcs = srcs, **kwargs) |
162 | 163 | ) |
163 | 164 |
|
| 165 | +def _find_namespace_package_files(rctx, install_dir): |
| 166 | + """Finds all `__init__.py` files that belong to namespace packages. |
| 167 | +
|
| 168 | + A `__init__.py` file belongs to a namespace package if it contains `__path__ =`, |
| 169 | + `pkgutil`, and `extend_path(`. |
| 170 | +
|
| 171 | + Args: |
| 172 | + rctx (repository_ctx): The repository context. |
| 173 | + install_dir (path): The path to the install directory. |
| 174 | +
|
| 175 | + Returns: |
| 176 | + list[str]: A list of relative paths to `__init__.py` files that belong |
| 177 | + to namespace packages. |
| 178 | + """ |
| 179 | + |
| 180 | + repo_root = str(rctx.path(".")) + "/" |
| 181 | + namespace_package_files = [] |
| 182 | + for top_level_dir in install_dir.readdir(): |
| 183 | + if not is_importable_name(top_level_dir.basename): |
| 184 | + continue |
| 185 | + init_py = top_level_dir.get_child("__init__.py") |
| 186 | + if not init_py.exists: |
| 187 | + continue |
| 188 | + content = rctx.read(init_py) |
| 189 | + |
| 190 | + # Look for code resembling the pkgutil namespace setup code: |
| 191 | + # __path__ = __import__("pkgutil").extend_path(__path__, __name__) |
| 192 | + if ("__path__ =" in content and |
| 193 | + "pkgutil" in content and |
| 194 | + "extend_path(" in content): |
| 195 | + namespace_package_files.append(str(init_py).removeprefix(repo_root)) |
| 196 | + |
| 197 | + return namespace_package_files |
| 198 | + |
164 | 199 | pypi_repo_utils = struct( |
165 | 200 | construct_pythonpath = _construct_pypath, |
166 | 201 | execute_checked = _execute_checked, |
167 | 202 | execute_checked_stdout = _execute_checked_stdout, |
| 203 | + find_namespace_package_files = _find_namespace_package_files, |
168 | 204 | resolve_python_interpreter = _resolve_python_interpreter, |
169 | 205 | ) |
0 commit comments