Skip to content

Commit 69956d2

Browse files
committed
Add install.hard_link_entrypoints config option.
Fixes #266
1 parent f94c696 commit 69956d2

File tree

4 files changed

+34
-6
lines changed

4 files changed

+34
-6
lines changed

src/manage/aliasutils.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,16 @@ def _if_exists(launcher, plat):
8585
return launcher
8686

8787

88-
def _create_alias(cmd, *, name, target, plat=None, windowed=0, script_code=None, _link=os.link):
88+
def _create_alias(
89+
cmd,
90+
*,
91+
name,
92+
target,
93+
plat=None,
94+
windowed=0,
95+
script_code=None,
96+
allow_link=True,
97+
_link=os.link):
8998
p = cmd.global_dir / name
9099
if not p.match("*.exe"):
91100
p = p.with_name(p.name + ".exe")
@@ -129,12 +138,27 @@ def _create_alias(cmd, *, name, target, plat=None, windowed=0, script_code=None,
129138
LOGGER.debug("Failed to read existing alias launcher.")
130139

131140
launcher_remap = cmd.scratch.setdefault("aliasutils.create_alias.launcher_remap", {})
132-
if existing_bytes == launcher_bytes:
141+
if not allow_link or not _link:
142+
# If links are disallowed, always replace the target with a copy.
143+
unlink(p)
144+
try:
145+
p.write_bytes(launcher_bytes)
146+
LOGGER.debug("Created %s as copy of %s", p.name, launcher.name)
147+
launcher_remap[launcher.name] = p
148+
except OSError:
149+
LOGGER.error("Failed to create global command %s.", name)
150+
LOGGER.debug("TRACEBACK", exc_info=True)
151+
elif existing_bytes == launcher_bytes:
133152
# Valid existing launcher, so save its path in case we need it later
134153
# for a hard link.
135154
launcher_remap.setdefault(launcher.name, p)
136155
else:
137-
# First try and create a hard link
156+
# Links are allowed and we need to create one, so try to make a link,
157+
# falling back to a link to another existing alias (that we've checked
158+
# already during this run), and then falling back to a copy.
159+
# This handles the case where our links are on a different volume to the
160+
# install (so hard links don't work), but limits us to only a single
161+
# copy (each) of the redirector(s), thus saving space.
138162
unlink(p)
139163
try:
140164
_link(launcher, p)
@@ -305,7 +329,7 @@ def calculate_aliases(cmd, install, *, _scan=_scan):
305329
yield ai.replace(target=default_alias.target)
306330

307331

308-
def create_aliases(cmd, aliases, *, _create_alias=_create_alias):
332+
def create_aliases(cmd, aliases, *, allow_link=True, _create_alias=_create_alias):
309333
if not cmd.global_dir:
310334
return
311335

@@ -337,6 +361,7 @@ def create_aliases(cmd, aliases, *, _create_alias=_create_alias):
337361
target=target,
338362
script_code=alias.script_code,
339363
windowed=alias.windowed,
364+
allow_link=allow_link,
340365
)
341366
except NoLauncherTemplateError:
342367
if install_matches_any(alias.install, getattr(cmd, "tags", None)):

src/manage/commands.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ def execute(self):
258258
"default_install_tag": (str, None),
259259
"preserve_site_on_upgrade": (config_bool, None),
260260
"enable_entrypoints": (config_bool, None),
261+
"hard_link_entrypoints": (config_bool, None),
261262
},
262263

263264
"first_run": {
@@ -823,6 +824,7 @@ class InstallCommand(BaseCommand):
823824
default_install_tag = None
824825
preserve_site_on_upgrade = True
825826
enable_entrypoints = True
827+
hard_link_entrypoints = True
826828

827829
def __init__(self, args, root=None):
828830
super().__init__(args, root)

src/manage/install_command.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def update_all_shortcuts(cmd, *, _aliasutils=None):
294294
except LookupError:
295295
LOGGER.warn("Failed to process aliases for %s.", i.get("display-name", i["id"]))
296296
LOGGER.debug("TRACEBACK", exc_info=True)
297-
_aliasutils.create_aliases(cmd, aliases)
297+
_aliasutils.create_aliases(cmd, aliases, allow_link=getattr(cmd, "hard_link_entrypoints", True))
298298
_aliasutils.cleanup_aliases(cmd, preserve=aliases)
299299

300300
for i in installs:

src/pymanager.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"install": {
33
"source": "%PYTHON_MANAGER_SOURCE_URL%",
44
"fallback_source": "./bundled/fallback-index.json",
5-
"default_install_tag": "3"
5+
"default_install_tag": "3",
6+
"hard_link_entrypoints": false
67
},
78
"list": {
89
"format": "%PYTHON_MANAGER_LIST_FORMAT%"

0 commit comments

Comments
 (0)