|
7 | 7 | import subprocess
|
8 | 8 | import sys
|
9 | 9 | import venv
|
| 10 | +from collections.abc import Mapping |
10 | 11 | from functools import cache
|
11 | 12 | from pathlib import Path
|
12 | 13 | from typing import NamedTuple
|
@@ -54,28 +55,57 @@ class PackageDependencies(NamedTuple):
|
54 | 55 | external_pkgs: tuple[str, ...]
|
55 | 56 |
|
56 | 57 |
|
| 58 | +@cache |
| 59 | +def get_pypi_name_to_typeshed_name_mapping() -> Mapping[str, str]: |
| 60 | + stub_name_map = {} |
| 61 | + for typeshed_name in os.listdir("stubs"): |
| 62 | + with Path("stubs", typeshed_name, "METADATA.toml").open("rb") as f: |
| 63 | + pypi_name = tomli.load(f).get("stub_distribution", f"types-{typeshed_name}") |
| 64 | + assert isinstance(pypi_name, str) |
| 65 | + stub_name_map[pypi_name] = typeshed_name |
| 66 | + return stub_name_map |
| 67 | + |
| 68 | + |
57 | 69 | @cache
|
58 | 70 | def read_dependencies(distribution: str) -> PackageDependencies:
|
| 71 | + """Read the dependencies listed in a METADATA.toml file for a stubs package. |
| 72 | +
|
| 73 | + Once the dependencies have been read, |
| 74 | + determine which dependencies are typeshed-internal dependencies, |
| 75 | + and which dependencies are external (non-types) dependencies. |
| 76 | + For typeshed dependencies, translate the "dependency name" into the "package name"; |
| 77 | + for external dependencies, leave them as they are in the METADATA.toml file. |
| 78 | +
|
| 79 | + Note that this function may consider things to be typeshed stubs |
| 80 | + even if they haven't yet been uploaded to PyPI. |
| 81 | + If a typeshed stub is removed, this function will consider it to be an external dependency. |
| 82 | + """ |
| 83 | + pypi_name_to_typeshed_name_mapping = get_pypi_name_to_typeshed_name_mapping() |
59 | 84 | with Path("stubs", distribution, "METADATA.toml").open("rb") as f:
|
60 | 85 | data = tomli.load(f)
|
61 | 86 | dependencies = data.get("requires", [])
|
62 | 87 | assert isinstance(dependencies, list)
|
63 | 88 | typeshed, external = [], []
|
64 | 89 | for dependency in dependencies:
|
65 | 90 | assert isinstance(dependency, str)
|
66 |
| - if dependency.startswith("types-"): |
67 |
| - maybe_typeshed_dependency = Requirement(dependency).name.removeprefix("types-") |
68 |
| - if maybe_typeshed_dependency in os.listdir("stubs"): |
69 |
| - typeshed.append(maybe_typeshed_dependency) |
70 |
| - else: |
71 |
| - external.append(dependency) |
| 91 | + maybe_typeshed_dependency = Requirement(dependency).name |
| 92 | + if maybe_typeshed_dependency in pypi_name_to_typeshed_name_mapping: |
| 93 | + typeshed.append(pypi_name_to_typeshed_name_mapping[maybe_typeshed_dependency]) |
72 | 94 | else:
|
73 | 95 | external.append(dependency)
|
74 | 96 | return PackageDependencies(tuple(typeshed), tuple(external))
|
75 | 97 |
|
76 | 98 |
|
77 | 99 | @cache
|
78 | 100 | def get_recursive_requirements(package_name: str) -> PackageDependencies:
|
| 101 | + """Recursively gather dependencies for a single stubs package. |
| 102 | +
|
| 103 | + For example, if the stubs for `caldav` |
| 104 | + declare a dependency on typeshed's stubs for `requests`, |
| 105 | + and the stubs for requests declare a dependency on typeshed's stubs for `urllib3`, |
| 106 | + `get_recursive_requirements("caldav")` will determine that the stubs for `caldav` |
| 107 | + have both `requests` and `urllib3` as typeshed-internal dependencies. |
| 108 | + """ |
79 | 109 | typeshed: set[str] = set()
|
80 | 110 | external: set[str] = set()
|
81 | 111 | non_recursive_requirements = read_dependencies(package_name)
|
|
0 commit comments