Skip to content

Commit 1e79f25

Browse files
authored
Expose is_editable_install and editable_install_path (#279)
* Expose `is_editable_install` and `editable_install_path` Could be useful to downstream projects. See, e.g., scipy/scipy#22887 (comment) * Fix type annotations for 3.9
1 parent 34fe529 commit 1e79f25

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

spin/cmds/meson.py

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
from enum import Enum
1010
from pathlib import Path
11+
from typing import Union
1112

1213
import click
1314

@@ -35,7 +36,15 @@ def _meson_cli():
3536
return [meson_cli]
3637

3738

38-
def _editable_install_path(distname):
39+
def editable_install_path(distname: str) -> Union[str, None]:
40+
"""Return path of the editable install for package `distname`.
41+
42+
If the package is not an editable install, return None.
43+
44+
See Also
45+
--------
46+
is_editable_install
47+
"""
3948
import importlib_metadata
4049

4150
try:
@@ -57,16 +66,34 @@ def _editable_install_path(distname):
5766
return None
5867

5968

60-
def _is_editable_install(distname):
61-
return _editable_install_path(distname) is not None
69+
# backward compat
70+
_editable_install_path = editable_install_path
71+
72+
73+
def is_editable_install(distname: str, verify_path: bool = False) -> bool:
74+
"""Whether or not an editable install of `distname` is present.
75+
76+
Parameters
77+
----------
78+
`distname` : str
79+
Name of the package. E.g., ``numpy`` or ``scikit-image``.
80+
Not always the same as the module name (``numpy`` and
81+
``skimage`` for the above).
82+
"""
83+
return editable_install_path(distname) is not None
84+
85+
86+
# backward compat
87+
_is_editable_install = is_editable_install
6288

6389

64-
def _is_editable_install_of_same_source(distname):
65-
editable_path = _editable_install_path(distname)
66-
return editable_path and os.path.samefile(_editable_install_path(distname), ".")
90+
def _is_editable_install_of_same_source(distname: str) -> bool:
91+
"""Check whether the editable install was made from the current directory."""
92+
editable_path = editable_install_path(distname)
93+
return (editable_path is not None) and os.path.samefile(editable_path, ".")
6794

6895

69-
def _set_pythonpath(build_dir, quiet=False):
96+
def _set_pythonpath(build_dir: str, quiet: bool = False) -> str:
7097
"""Set first entry of PYTHONPATH to site packages directory.
7198
7299
For editable installs, leave the PYTHONPATH alone.
@@ -78,7 +105,7 @@ def _set_pythonpath(build_dir, quiet=False):
78105
cfg = get_config()
79106
distname = cfg.get("project.name", None)
80107
if distname:
81-
if _is_editable_install(distname):
108+
if is_editable_install(distname):
82109
if _is_editable_install_of_same_source(distname):
83110
if not (quiet):
84111
click.secho(
@@ -114,11 +141,11 @@ def _set_pythonpath(build_dir, quiet=False):
114141
return site_packages
115142

116143

117-
def _get_install_dir(build_dir):
144+
def _get_install_dir(build_dir: str) -> str:
118145
return f"{build_dir}-install"
119146

120147

121-
def _get_site_packages(build_dir):
148+
def _get_site_packages(build_dir: str) -> str:
122149
install_dir = _get_install_dir(build_dir)
123150
try:
124151
cfg = get_config()
@@ -140,13 +167,12 @@ def _get_site_packages(build_dir):
140167
site_packages = None
141168
if any(f"python{X}." in p for p in candidate_paths):
142169
# We have a system that uses `python3.X/site-packages` or `python3.X/dist-packages`
143-
site_packages = [p for p in candidate_paths if f"python{X}.{Y}" in p]
144-
if len(site_packages) == 0:
170+
site_packages_paths = [p for p in candidate_paths if f"python{X}.{Y}" in p]
171+
if len(site_packages_paths) == 0:
145172
raise FileNotFoundError(
146173
f"No site-packages found in {install_dir} for Python {X}.{Y}"
147174
)
148-
else:
149-
site_packages = site_packages[0]
175+
site_packages = site_packages_paths[0]
150176
else:
151177
# A naming scheme that does not encode the Python major/minor version is used, so return
152178
# whatever site-packages path was found
@@ -165,22 +191,22 @@ def _get_site_packages(build_dir):
165191
return site_packages
166192

167193

168-
def _meson_version():
194+
def _meson_version() -> Union[str, None]:
169195
try:
170196
p = _run(_meson_cli() + ["--version"], output=False, echo=False)
171197
return p.stdout.decode("ascii").strip()
172198
except:
173-
pass
199+
return None
174200

175201

176-
def _meson_version_configured(build_dir):
202+
def _meson_version_configured(build_dir: str) -> Union[str, None]:
177203
try:
178204
meson_info_fn = os.path.join(build_dir, "meson-info", "meson-info.json")
179205
with open(meson_info_fn) as f:
180206
meson_info = json.load(f)
181207
return meson_info["meson_version"]["full"]
182208
except:
183-
pass
209+
return None
184210

185211

186212
def _meson_coverage_configured() -> bool:
@@ -199,7 +225,7 @@ def _meson_coverage_configured() -> bool:
199225
return False
200226

201227

202-
def _check_coverage_tool_installation(coverage_type: GcovReportFormat, build_dir):
228+
def _check_coverage_tool_installation(coverage_type: GcovReportFormat, build_dir: str):
203229
requirements = { # https://github.com/mesonbuild/meson/blob/6e381714c7cb15877e2bcaa304b93c212252ada3/docs/markdown/Unit-tests.md?plain=1#L49-L62
204230
GcovReportFormat.html: ["Gcovr/GenHTML", "lcov"],
205231
GcovReportFormat.xml: ["Gcovr (version 3.3 or higher)"],

spin/cmds/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def parent_cmd(*user_args, **user_kwargs):
168168
return user_func(*args, parent_callback=parent_cmd, **kwargs)
169169

170170
my_cmd.callback = click.pass_context(callback_with_parent_callback)
171-
my_cmd.callback._parent = user_func
171+
my_cmd.callback._parent = user_func # type: ignore[attr-defined]
172172

173173
if doc is not None:
174174
my_cmd.help = doc

0 commit comments

Comments
 (0)