• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 20974506033

13 Jan 2026 10:14PM UTC coverage: 43.251% (-37.0%) from 80.269%
20974506033

Pull #22976

github

web-flow
Merge a16a40040 into c12556724
Pull Request #22976: WIP: Add the ability to set stdin for a Process

2 of 4 new or added lines in 2 files covered. (50.0%)

17213 existing lines in 540 files now uncovered.

26146 of 60452 relevant lines covered (43.25%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.67
/src/python/pants/backend/python/dependency_inference/default_module_mapping.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
# NB: The project names must follow the naming scheme at
5
#  https://www.python.org/dev/peps/pep-0503/#normalized-names.
6

7
import re
2✔
8
from collections.abc import Callable
2✔
9
from enum import Enum
2✔
10
from functools import partial
2✔
11
from re import Match
2✔
12

13

14
class PackageSeparator(Enum):
2✔
15
    DOT = "."
2✔
16
    UNDERSCORE = "_"
2✔
17
    NONE = ""
2✔
18

19

20
def all_hyphen_to_separator(m: Match[str], separator: PackageSeparator) -> str:
2✔
21
    """Convert all hyphens to a package separator e.g. azure-foo-bar -> azure.foo.bar or
22
    azure_foo_bar.
23

24
    >>> all_hyphen_to_separator(re.match(r"^azure-.+", "azure-foo-bar"), PackageSeparator.DOT)
25
    'azure.foo.bar'
26
    >>> all_hyphen_to_separator(re.match(r"^azure-.+", "azure-foo-bar"), PackageSeparator.UNDERSCORE)
27
    'azure_foo_bar'
28
    >>> all_hyphen_to_separator(re.match(r"^azure-.+", "azure-foo-bar"), PackageSeparator.NONE)
29
    'azurefoobar'
30
    """
UNCOV
31
    return m.string.replace("-", separator.value)
×
32

33

34
def first_group_hyphen_to_separator(m: Match[str], separator: PackageSeparator) -> str:
2✔
35
    """Convert the first group(regex match group) of hyphens to underscores. Only returns the first
36
    group and must contain at least one group.
37

38
    >>> first_group_hyphen_to_separator(re.match(r"^django-((.+(-.+)?))", "django-admin-cursor-paginator"), separator=PackageSeparator.UNDERSCORE)
39
    'admin_cursor_paginator'
40
    >>> first_group_hyphen_to_separator(re.match(r"^django-((.+(-.+)?))", "django-admin-cursor-paginator"), separator=PackageSeparator.DOT)
41
    'admin.cursor.paginator'
42
    >>> first_group_hyphen_to_separator(re.match(r"^django-((.+(-.+)?))", "django-admin-cursor-paginator"), separator=PackageSeparator.NONE)
43
    'admincursorpaginator'
44
    """
UNCOV
45
    if m.re.groups == 0 or not m.groups():
×
UNCOV
46
        raise ValueError(f"expected at least one group in the pattern{m.re.pattern} but got none.")
×
UNCOV
47
    return str(m.groups()[0]).replace("-", separator.value)
×
48

49

50
def two_groups_hyphens_two_replacements_with_suffix(
2✔
51
    m: Match[str],
52
    first_group_replacement: PackageSeparator = PackageSeparator.DOT,
53
    second_group_replacement: PackageSeparator = PackageSeparator.NONE,
54
    custom_suffix: str = "",
55
) -> str:
56
    """take two groups, and by default, the first will have '-' replaced with '.', the second will
57
    have '-' replaced with '' e.g. google-cloud-foo-bar -> group1(google.cloud.)group2(foobar)
58

59
    >>> two_groups_hyphens_two_replacements_with_suffix(re.match(r"^(google-cloud-)([^.]+)", "google-cloud-foo-bar"))
60
    'google.cloud.foobar'
61
    >>> two_groups_hyphens_two_replacements_with_suffix(re.match(r"^(google-cloud-)([^.]+)", "google-cloud-foo-bar"), first_group_replacement=PackageSeparator.UNDERSCORE, second_group_replacement=PackageSeparator.DOT)
62
    'google_cloud_foo.bar'
63
    """
UNCOV
64
    if m.re.groups < 2 or not m.groups():
×
UNCOV
65
        raise ValueError(f"expected at least two groups in the pattern{m.re.pattern}.")
×
UNCOV
66
    prefix = m.string[m.start(1) : m.end(1)].replace("-", first_group_replacement.value)
×
UNCOV
67
    suffix = m.string[m.start(2) : m.end(2)].replace("-", second_group_replacement.value)
×
UNCOV
68
    return f"{prefix}{suffix}{custom_suffix}"
×
69

70

71
# common replacement methods
72
all_hyphen_to_dot = partial(all_hyphen_to_separator, separator=PackageSeparator.DOT)
2✔
73
all_hyphen_to_underscore = partial(all_hyphen_to_separator, separator=PackageSeparator.UNDERSCORE)
2✔
74
first_group_hyphen_to_dot = partial(first_group_hyphen_to_separator, separator=PackageSeparator.DOT)
2✔
75
first_group_hyphen_to_underscore = partial(
2✔
76
    first_group_hyphen_to_separator, separator=PackageSeparator.UNDERSCORE
77
)
78

79
"""
2✔
80
A mapping of Patterns and their replacements. will be used with `re.sub`.
81
The match is either a string or a function`(str) -> str`; that takes a re.Match and returns
82
the replacement. see re.sub for more information
83

84
then if an import in the python code is google.cloud.foo, then the package of
85
google-cloud-foo will be used.
86
"""
87
DEFAULT_MODULE_PATTERN_MAPPING: dict[re.Pattern, list[Callable[[Match[str]], str]]] = {
2✔
88
    re.compile(r"""^azure-.+"""): [all_hyphen_to_dot],
89
    re.compile(r"""^django-((.+(-.+)?))"""): [first_group_hyphen_to_underscore],
90
    # See https://github.com/googleapis/google-cloud-python#libraries for all Google cloud
91
    # libraries. We only add libraries in GA, not beta.
92
    re.compile(r"""^(google-cloud-)([^.]+)"""): [
93
        partial(two_groups_hyphens_two_replacements_with_suffix, custom_suffix=custom_suffix)
94
        for custom_suffix in ("", "_v1", "_v2", "_v3")
95
    ],
96
    re.compile(r"""^(opentelemetry-instrumentation-)([^.]+)"""): [
97
        partial(
98
            two_groups_hyphens_two_replacements_with_suffix,
99
            second_group_replacement=PackageSeparator.UNDERSCORE,
100
        ),
101
    ],
102
    re.compile(r"""^oslo-.+"""): [all_hyphen_to_underscore],
103
    re.compile(r"""^python-(.+)"""): [first_group_hyphen_to_underscore],
104
    re.compile(r"""^apache-(airflow-providers-.+)"""): [first_group_hyphen_to_dot],
105
}
106

107
DEFAULT_MODULE_MAPPING: dict[str, tuple[str, ...]] = {
2✔
108
    "absl-py": ("absl",),
109
    "acryl-datahub": ("datahub",),
110
    "ansicolors": ("colors",),
111
    "antlr4-python3-runtime": ("antlr4",),
112
    "apache-airflow": ("airflow",),
113
    "apache-airflow-client": ("airflow_client",),
114
    "atlassian-python-api": ("atlassian",),
115
    "attrs": ("attr", "attrs"),
116
    "auth0-python": ("auth0",),
117
    "beautifulsoup4": ("bs4",),
118
    "biopython": ("Bio", "BioSQL"),
119
    "bitvector": ("BitVector",),
120
    "cattrs": ("cattr", "cattrs"),
121
    "cloud-sql-python-connector": ("google.cloud.sql.connector",),
122
    "confluent-kafka": ("confluent_kafka",),
123
    "coolprop": ("CoolProp",),
124
    "databricks-sdk": ("databricks.sdk",),
125
    "databricks-sql-connector": (
126
        "databricks.sql",
127
        "databricks.sqlalchemy",
128
    ),
129
    "delta-spark": ("delta",),
130
    "discord-py": ("discord",),
131
    "django-activity-stream": ("actstream",),
132
    "django-cors-headers": ("corsheaders",),
133
    "django-countries": ("django_countries",),
134
    "django-filter": ("django_filters",),
135
    "django-fsm": ("django_fsm",),
136
    "django-notifications-hq": ("notifications",),
137
    "django-oauth-toolkit": ("oauth2_provider",),
138
    "django-object-actions": ("django_object_actions",),
139
    "django-postgres-extra": ("psqlextra",),
140
    "django-redis": ("django_redis",),
141
    "django-scim2": ("django_scim",),
142
    "django-two-factor-auth": ("two_factor",),
143
    "djangorestframework": ("rest_framework",),
144
    "djangorestframework-api-key": ("rest_framework_api_key",),
145
    "djangorestframework-dataclasses": ("rest_framework_dataclasses",),
146
    "djangorestframework-jwt": ("rest_framework_jwt",),
147
    "djangorestframework-queryfields": ("drf_queryfields",),
148
    "djangorestframework-simplejwt": ("rest_framework_simplejwt",),
149
    "dnspython": ("dns",),
150
    "drf-api-tracking": ("rest_framework_tracking",),
151
    "elastic-apm": ("elasticapm",),
152
    "enum34": ("enum",),
153
    "factory-boy": ("factory",),
154
    "faiss-cpu": ("faiss",),
155
    "faiss-gpu": ("faiss",),
156
    "fluent-logger": ("fluent",),
157
    "fonttools": ("fontTools",),
158
    "gitpython": ("git",),
159
    "google-api-python-client": ("googleapiclient",),
160
    "google-auth": (
161
        "google.auth",
162
        "google.oauth2",
163
    ),
164
    "graphql-core": ("graphql",),
165
    "grpcio": ("grpc",),
166
    "grpcio-channelz": ("grpcio_channelz",),
167
    "grpcio-health-checking": ("grpc_health",),
168
    "grpcio-reflection": ("grpc_reflection",),
169
    "grpcio-status": ("grpc_status",),
170
    "grpcio-testing": ("grpc_testing",),
171
    "hdrhistogram": ("hdrh",),
172
    "honeycomb-opentelemetry": ("honeycomb.opentelemetry",),
173
    "ipython": ("IPython",),
174
    "jack-client": ("jack",),
175
    "kafka-python": ("kafka",),
176
    "lark-parser": ("lark",),
177
    "launchdarkly-server-sdk": ("ldclient",),
178
    "mail-parser": ("mailparser",),
179
    "matplotlib": ("matplotlib", "mpl_toolkits"),
180
    "matrix-nio": ("nio",),
181
    "mysql-connector-python": ("mysql.connector",),
182
    "netcdf4": ("netCDF4",),
183
    "o365": ("O365",),
184
    "opencv-python": ("cv2",),
185
    "opencv-python-headless": ("cv2",),
186
    "opensearch-py": ("opensearchpy",),
187
    # opentelemetry
188
    "opentelemetry-api": (
189
        "opentelemetry._logs",
190
        "opentelemetry.attributes",
191
        "opentelemetry.baggage",
192
        "opentelemetry.context",
193
        "opentelemetry.environment_variables",
194
        "opentelemetry.metrics",
195
        "opentelemetry.propagate",
196
        "opentelemetry.propagators",
197
        "opentelemetry.trace",
198
        "opentelemetry.util",
199
        "opentelemetry.version",
200
    ),
201
    "opentelemetry-exporter-otlp": ("opentelemetry.exporter.otlp",),
202
    "opentelemetry-exporter-otlp-proto-grpc": ("opentelemetry.exporter.otlp.proto.grpc",),
203
    "opentelemetry-exporter-otlp-proto-http": ("opentelemetry.exporter.otlp.proto.http",),
204
    "opentelemetry-instrumentation-kafka-python": ("opentelemetry.instrumentation.kafka",),
205
    "opentelemetry-proto": ("opentelemetry.proto",),
206
    "opentelemetry-sdk": ("opentelemetry.sdk",),
207
    "opentelemetry-semantic-conventions": ("opentelemetry.semconv",),
208
    "opentelemetry-test-utils": ("opentelemetry.test",),
209
    "paho-mqtt": ("paho",),
210
    "phonenumberslite": ("phonenumbers",),
211
    "pillow": ("PIL",),
212
    "pip-tools": ("piptools",),
213
    "progressbar2": ("progressbar",),
214
    "protobuf": ("google.protobuf",),
215
    "psycopg2-binary": ("psycopg2",),
216
    "py-healthcheck": ("healthcheck",),
217
    "pycrypto": ("Crypto",),
218
    "pycryptodome": ("Crypto",),
219
    "pyerfa": ("erfa",),
220
    "pygithub": ("github",),
221
    "pygobject": ("gi",),
222
    "pyhamcrest": ("hamcrest",),
223
    "pyjwt": ("jwt",),
224
    "pykube-ng": ("pykube",),
225
    "pymongo": ("bson", "gridfs", "pymongo"),
226
    "pymupdf": ("fitz", "pymupdf"),
227
    "pynacl": ("nacl",),
228
    "pyopenssl": ("OpenSSL",),
229
    "pypdf2": ("PyPDF2",),
230
    "pypi-kenlm": ("kenlm",),
231
    "pyshp": ("shapefile",),
232
    "pysocks": ("socks",),
233
    "pytest": ("pytest", "_pytest"),
234
    "pytest-runner": ("ptr",),
235
    "python-sat": ("pysat",),
236
    "python-json-logger": ("pythonjsonlogger",),
237
    "python-levenshtein": ("Levenshtein",),
238
    "python-lsp-jsonrpc": ("pylsp_jsonrpc",),
239
    "pywinrm": ("winrm",),
240
    "pywavelets": ("pywt",),
241
    "pyyaml": ("yaml",),
242
    "randomwords": ("random_words",),
243
    "robotraconteur": ("RobotRaconteur",),
244
    "scikit-image": ("skimage",),
245
    "scikit-learn": ("sklearn",),
246
    "scikit-video": ("skvideo",),
247
    "sisl": ("sisl", "sisl_toolbox"),
248
    "setuptools": ("easy_install", "pkg_resources", "setuptools"),
249
    "snowflake-connector-python": ("snowflake.connector",),
250
    "snowflake-snowpark-python": ("snowflake.snowpark",),
251
    "snowflake-sqlalchemy": ("snowflake.sqlalchemy",),
252
    "sseclient-py": ("sseclient",),
253
    "strawberry-graphql": ("strawberry",),
254
    "streamlit-aggrid": ("st_aggrid",),
255
    "unleashclient": ("UnleashClient",),
256
    "websocket-client": ("websocket",),
257
}
258

259
DEFAULT_TYPE_STUB_MODULE_PATTERN_MAPPING: dict[re.Pattern, list[Callable[[Match[str]], str]]] = {
2✔
260
    re.compile(r"""^stubs[_-](.+)"""): [first_group_hyphen_to_underscore],
261
    re.compile(r"""^types[_-](.+)"""): [first_group_hyphen_to_underscore],
262
    re.compile(r"""^(.+)[_-]stubs"""): [first_group_hyphen_to_underscore],
263
    re.compile(r"""^(.+)[_-]types"""): [first_group_hyphen_to_underscore],
264
}
265

266
DEFAULT_TYPE_STUB_MODULE_MAPPING: dict[str, tuple[str, ...]] = {
2✔
267
    "djangorestframework-types": ("rest_framework",),
268
    "lark-stubs": ("lark",),
269
    "types-beautifulsoup4": ("bs4",),
270
    "types-enum34": ("enum34",),
271
    "types-grpcio": ("grpc",),
272
    "types-grpcio-channelz": ("grpcio_channelz",),
273
    "types-grpcio-health-checking": ("grpc_health",),
274
    "types-grpcio-reflection": ("grpc_reflection",),
275
    "types-grpcio-status": ("grpc_status",),
276
    "types-pillow": ("PIL",),
277
    "types-protobuf": ("google.protobuf",),
278
    "types-pycrypto": ("Crypto",),
279
    "types-pyopenssl": ("OpenSSL",),
280
    "types-pyyaml": ("yaml",),
281
    "types-python-dateutil": ("dateutil",),
282
    "types-setuptools": ("easy_install", "pkg_resources", "setuptools"),
283
}
284

285
if __name__ == "__main__":
2✔
286
    import doctest
×
287

288
    doctest.testmod()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc