Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fc64f28
[WIP] Start implementation of entrypoint support
zooba Nov 20, 2025
8b83213
[WIP] Updated, implemented, and existing tests pass
zooba Nov 24, 2025
7ff8626
Add test
zooba Nov 25, 2025
1263c28
Improved script and fixed names
zooba Nov 25, 2025
fc4793f
Fix tests
zooba Nov 25, 2025
bd20de5
Fix tests
zooba Nov 25, 2025
36118d8
Add tests to improve coverage
zooba Nov 26, 2025
81808f3
More test coverage
zooba Nov 26, 2025
2a3839b
Minor refactor, improved test coverage
zooba Nov 27, 2025
16a60c1
Remove unused import
zooba Nov 27, 2025
52d27a7
Add comment and update scratch key
zooba Nov 27, 2025
e05fbad
Add some missing log messages
zooba Nov 27, 2025
e621dd4
Minor bug fixes
zooba Nov 27, 2025
5916e76
Properly handle launching script executable (not DLL)
zooba Nov 27, 2025
5be8d04
Ensure pip.exe exists
zooba Dec 2, 2025
0a3924e
Merge main
zooba Dec 3, 2025
2069514
Add welcome message
zooba Dec 3, 2025
fddc8cc
Add refresh step to entrypoint test
zooba Dec 3, 2025
c67f0e5
Fix paths
zooba Dec 3, 2025
7f24d36
Minor refactoring on alias creation
zooba Dec 3, 2025
ea6d7c7
Fix calls
zooba Dec 3, 2025
935ab05
Merge main
zooba Dec 8, 2025
198a73f
Refactor and simplify code for aliases
zooba Dec 9, 2025
2eec6d2
Improved edge case handling and test
zooba Dec 9, 2025
a36f76a
Update args
zooba Dec 9, 2025
fb9b7c0
Remove some dead code
zooba Dec 9, 2025
13c57b7
Naming conventions
zooba Dec 9, 2025
42b51bb
Fixes and improvements suggested by reviewer
zooba Dec 9, 2025
753b295
Split names before testing
zooba Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Minor refactoring on alias creation
  • Loading branch information
zooba committed Dec 3, 2025
commit 7f24d367ce688dda3fbe1503608bf911d8cbeeb8
21 changes: 5 additions & 16 deletions src/manage/aliasutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def _if_exists(launcher, plat):
return launcher


def create_alias(cmd, install, alias, target, *, script_code=None, _link=os.link):
def create_alias(cmd, install, alias, target, aliases_written, *, script_code=None, _link=os.link):
p = cmd.global_dir / alias["name"]
if not p.match("*.exe"):
p = p.with_name(p.name + ".exe")
Expand All @@ -56,12 +56,11 @@ def create_alias(cmd, install, alias, target, *, script_code=None, _link=os.link
if alias.get("windowed"):
launcher = cmd.launcherw_exe or launcher

alias_written = cmd.scratch.setdefault("aliasutils.create_alias.alias_written", set())
n = p.stem.casefold()
if n in alias_written:
if n in aliases_written:
# We've already written this alias in this session, so skip it.
return
alias_written.add(n)
aliases_written.add(n)

plat = install["tag"].rpartition("-")[-1]
if plat:
Expand Down Expand Up @@ -231,13 +230,9 @@ def _scan(prefix, dirs):
yield from _scan_one(root)


def scan_and_create_entrypoints(cmd, install, shortcut, *, _create_alias=create_alias, _scan=_scan):
def scan_and_create_entrypoints(cmd, install, shortcut, aliases_written, *, _create_alias=create_alias, _scan=_scan):
prefix = install["prefix"]

# We will be called multiple times, so need to keep the list of names we've
# already used in this session.
known = cmd.scratch.setdefault("aliasutils.scan_and_create_entrypoints.known", set())

aliases = list(install.get("alias", ()))
alias_1 = [a for a in aliases if not a.get("windowed")]
# If no windowed targets, we'll use the non-windowed one
Expand All @@ -254,19 +249,13 @@ def scan_and_create_entrypoints(cmd, install, shortcut, *, _create_alias=create_
return

for alias, code in _scan(prefix, shortcut.get("dirs")):
# Only create names once per install command
n = alias["name"].casefold()
if n in known:
continue
known.add(n)

# Copy the launcher template and create a standard __target__ file
target = targets[1 if alias.get("windowed", 0) else 0]
if not target:
LOGGER.debug("No suitable alias found for %s. Skipping this " +
"entrypoint", alias["name"])
continue
_create_alias(cmd, install, alias, target, script_code=code)
_create_alias(cmd, install, alias, target, aliases_written, script_code=code)


def cleanup_alias(cmd, site_dirs_written, *, _unlink_many=atomic_unlink, _scan=_scan):
Expand Down
3 changes: 2 additions & 1 deletion src/manage/install_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ def _cleanup_arp_entries(cmd, install_shortcut_pairs):

def _create_entrypoints(cmd, install, shortcut):
from .aliasutils import scan_and_create_entrypoints
scan_and_create_entrypoints(cmd, install, shortcut)
aliases_written = cmd.scratch.setdefault("aliasutils.create_alias.aliases_written", set())
scan_and_create_entrypoints(cmd, install, shortcut, aliases_written)


def _cleanup_entrypoints(cmd, install_shortcut_pairs):
Expand Down
18 changes: 17 additions & 1 deletion tests/test_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ def __exit__(self, *exc_info):
pass

def check(self, cmd, tag, name, expect, windowed=0, script_code=None):
created = set()
AU.create_alias(
cmd,
{"tag": tag},
{"name": name, "windowed": windowed},
self._expect_target,
created,
script_code=script_code,
)
print(*cmd.global_dir.glob("*"), sep="\n")
Expand All @@ -64,7 +66,7 @@ def check(self, cmd, tag, name, expect, windowed=0, script_code=None):
if script_code:
assert (cmd.global_dir / f"{name}.exe.__script__.py").is_file()
assert (cmd.global_dir / f"{name}.exe.__script__.py").read_text() == script_code
assert name.casefold() in cmd.scratch["aliasutils.create_alias.alias_written"]
assert name.casefold() in created

def check_32(self, cmd, tag, name):
self.check(cmd, tag, name, self._expect["-32"])
Expand Down Expand Up @@ -122,11 +124,13 @@ def test_write_alias_launcher_missing(fake_config, assert_log, tmp_path):
fake_config.launcher_exe = tmp_path / "non-existent.exe"
fake_config.default_platform = '-32'
fake_config.global_dir = tmp_path / "bin"
created = set()
AU.create_alias(
fake_config,
{"tag": "test"},
{"name": "test.exe"},
tmp_path / "target.exe",
created,
)
assert_log(
"Checking for launcher.*",
Expand All @@ -136,6 +140,7 @@ def test_write_alias_launcher_missing(fake_config, assert_log, tmp_path):
"Skipping %s alias because the launcher template was not found.",
assert_log.end_of_log(),
)
assert "test".casefold() in created


def test_write_alias_launcher_unreadable(fake_config, assert_log, tmp_path):
Expand All @@ -156,11 +161,13 @@ def read_bytes():
fake_config.launcher_exe = FakeLauncherPath
fake_config.default_platform = '-32'
fake_config.global_dir = tmp_path / "bin"
created = set()
AU.create_alias(
fake_config,
{"tag": "test"},
{"name": "test.exe"},
tmp_path / "target.exe",
created,
)
assert_log(
"Checking for launcher.*",
Expand All @@ -169,6 +176,7 @@ def read_bytes():
"Failed to read %s",
assert_log.end_of_log(),
)
assert "test".casefold() in created


def test_write_alias_launcher_unlinkable(fake_config, assert_log, tmp_path):
Expand All @@ -180,11 +188,13 @@ def fake_link(x, y):
fake_config.launcher_exe.write_bytes(b'Arbitrary contents')
fake_config.default_platform = '-32'
fake_config.global_dir = tmp_path / "bin"
created = set()
AU.create_alias(
fake_config,
{"tag": "test"},
{"name": "test.exe"},
tmp_path / "target.exe",
created,
_link=fake_link
)
assert_log(
Expand All @@ -194,6 +204,7 @@ def fake_link(x, y):
"Created %s as copy of %s",
assert_log.end_of_log(),
)
assert "test".casefold() in created


def test_write_alias_launcher_unlinkable_remap(fake_config, assert_log, tmp_path):
Expand All @@ -216,11 +227,13 @@ def fake_link(x, y):
(tmp_path / "actual_launcher.txt").write_bytes(b'Arbitrary contents')
fake_config.default_platform = '-32'
fake_config.global_dir = tmp_path / "bin"
created = set()
AU.create_alias(
fake_config,
{"tag": "test"},
{"name": "test.exe"},
tmp_path / "target.exe",
created,
_link=fake_link
)
assert_log(
Expand All @@ -230,6 +243,7 @@ def fake_link(x, y):
("Created %s as hard link to %s", ("test.exe", "actual_launcher.txt")),
assert_log.end_of_log(),
)
assert "test".casefold() in created


def test_parse_entrypoint_line():
Expand Down Expand Up @@ -266,6 +280,7 @@ def test_scan_create_entrypoints(fake_config, tmp_path):
fake_config,
install,
dict(dirs=["site-packages"]),
set(),
_create_alias=lambda *a, **kw: created.append((a, kw)),
)
assert 2 == len(created)
Expand Down Expand Up @@ -312,6 +327,7 @@ def fake_scan(*a):
fake_config,
dict(prefix=fake_config.root, id="test", alias=alias),
{},
set(),
_create_alias=lambda *a, **kw: created.append((a, kw)),
_scan=fake_scan,
)
Expand Down
1 change: 1 addition & 0 deletions tests/test_install_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def test_preserve_site(tmp_path):
preserved = tmp_path / "_root"
site = root / "site-packages"
not_site = root / "site-not-packages"
not_site.mkdir(parents=True, exist_ok=True)
A = site / "A"
B = site / "B.txt"
C = site / "C.txt"
Expand Down
Loading