Skip to content

Commit fc64f28

Browse files
committed
[WIP] Start implementation of entrypoint support
1 parent 2cd04aa commit fc64f28

File tree

2 files changed

+92
-36
lines changed

2 files changed

+92
-36
lines changed

src/manage/entrypointutils.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
def _scan(prefix, dirs):
3+
for dirname in dirs or ():
4+
# TODO: Handle invalid entries
5+
d = install["prefix"] / dirname
6+
7+
# TODO: Scan d for dist-info directories with entry_points.txt
8+
# Filter down to [console_scripts] and [gui_scripts]
9+
10+
# TODO: Yield the alias name and script contents
11+
# import sys; from <mod> import <func>; sys.exit(<func>())
12+
13+
14+
def scan_and_create(cmd, install, shortcut):
15+
for name, code in _scan(install["prefix"], shortcut.get("dirs")):
16+
# TODO: Store name in cmd's metadata.
17+
# If it's already been stored, skip all further processing.
18+
19+
# TOOD: Copy the launcher template and create a standard __target__ file
20+
# Also create an <alias>-script.py file containing code
21+
# pymanager/launcher.cpp wil need to be updated to use this script.
22+
# Regular alias creation will need to delete these scripts.
23+
24+
25+
def cleanup(cmd, install_shortcut_pairs):
26+
seen_names = set()
27+
for install, shortcut in install_shortcut_pairs:
28+
for name, code in _scan(install["prefix"], shortcut.get("dirs")):
29+
seen_names.add(name)
30+
31+
# TODO: Scan existing aliases, filter to those with -script.py files
32+
33+
# TODO: Excluding any in seen_names, delete unused aliases

src/manage/install_command.py

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
DOWNLOAD_CACHE = {}
2626

2727

28+
DEFAULT_SITE_DIRS = ["Lib\\site-packages", "Scripts"]
29+
2830
def _multihash(file, hashes):
2931
import hashlib
3032
LOGGER.debug("Calculating hashes: %s", ", ".join(hashes))
@@ -346,10 +348,21 @@ def _cleanup_arp_entries(cmd, install_shortcut_pairs):
346348
cleanup([i for i, s in install_shortcut_pairs], cmd.tags)
347349

348350

351+
def _create_entrypoints(cmd, install, shortcut):
352+
from .entrypointutils import scan_and_create
353+
scan_and_create(cmd, install, shortcut)
354+
355+
356+
def _cleanup_entrypoints(cmd, install_shortcut_pairs):
357+
from .entrypointutils import cleanup
358+
cleanup(cmd, install_shortcut_pairs)
359+
360+
349361
SHORTCUT_HANDLERS = {
350362
"pep514": (_create_shortcut_pep514, _cleanup_shortcut_pep514),
351363
"start": (_create_start_shortcut, _cleanup_start_shortcut),
352364
"uninstall": (_create_arp_entry, _cleanup_arp_entries),
365+
"site-dirs": (_create_entrypoints, _cleanup_entrypoints),
353366
}
354367

355368

@@ -396,6 +409,16 @@ def update_all_shortcuts(cmd):
396409
create(cmd, i, s)
397410
shortcut_written.setdefault(s["kind"], []).append((i, s))
398411

412+
# Earlier releases may not have site_dirs. If not, assume
413+
if ("site-dirs" in (cmd.enable_shortcut_kinds or ("site-dirs",)) and
414+
"site-dirs" not in (cmd.disable_shortcut_kinds or ()) and
415+
all(s["kind"] != "site-dirs" for s in i.get("shortcuts", ()))):
416+
417+
create, cleanup = SHORTCUT_HANDLERS["site-dirs"]
418+
s = dict(kind="site-dirs", dirs=DEFAULT_SITE_DIRS)
419+
create(cmd, i, s)
420+
shortcut_written.setdefault("site-dirs", []).append((i, s))
421+
399422
if cmd.global_dir and cmd.global_dir.is_dir() and cmd.launcher_exe:
400423
for target in cmd.global_dir.glob("*.exe.__target__"):
401424
alias = target.with_suffix("")
@@ -522,15 +545,7 @@ def _download_one(cmd, source, install, download_dir, *, must_copy=False):
522545
return package
523546

524547

525-
def _should_preserve_on_upgrade(cmd, root, path):
526-
if path.match("site-packages"):
527-
return True
528-
if path.parent == root and path.match("Scripts"):
529-
return True
530-
return False
531-
532-
533-
def _preserve_site(cmd, root):
548+
def _preserve_site(cmd, root, install):
534549
if not root.is_dir():
535550
return None
536551
if not cmd.preserve_site_on_upgrade:
@@ -542,39 +557,47 @@ def _preserve_site(cmd, root):
542557
if cmd.repair:
543558
LOGGER.verbose("Not preserving site directory because of --repair")
544559
return None
560+
545561
state = []
546562
i = 0
547-
dirs = [root]
563+
564+
site_dirs = DEFAULT_SITE_DIRS
565+
for s in install.get("shortcuts", ()):
566+
if s["kind"] == "site-dirs":
567+
site_dirs = s.get("dirs", ())
568+
break
569+
548570
target_root = root.with_name(f"_{root.name}")
549571
target_root.mkdir(parents=True, exist_ok=True)
550-
while dirs:
551-
if _should_preserve_on_upgrade(cmd, root, dirs[0]):
552-
while True:
553-
target = target_root / str(i)
554-
i += 1
555-
try:
556-
unlink(target)
557-
break
558-
except FileNotFoundError:
559-
break
560-
except OSError:
561-
LOGGER.verbose("Failed to remove %s.", target)
562-
try:
563-
LOGGER.info("Preserving %s during update.", dirs[0].relative_to(root))
564-
except ValueError:
565-
# Just in case a directory goes weird, so we don't break
566-
LOGGER.verbose(exc_info=True)
567-
LOGGER.verbose("Moving %s to %s", dirs[0], target)
572+
573+
for dirname in site_dirs:
574+
d = root / dirname
575+
if not d.is_dir():
576+
continue
577+
578+
while True:
579+
target = target_root / str(i)
580+
i += 1
568581
try:
569-
dirs[0].rename(target)
582+
unlink(target)
583+
break
584+
except FileNotFoundError:
585+
break
570586
except OSError:
571-
LOGGER.warn("Failed to preserve %s during update.", dirs[0])
572-
LOGGER.verbose("TRACEBACK", exc_info=True)
573-
else:
574-
state.append((dirs[0], target))
587+
LOGGER.verbose("Failed to remove %s.", target)
588+
try:
589+
LOGGER.info("Preserving %s during update.", d.relative_to(root))
590+
except ValueError:
591+
# Just in case a directory goes weird, so we don't break
592+
LOGGER.verbose(exc_info=True)
593+
LOGGER.verbose("Moving %s to %s", d, target)
594+
try:
595+
d.rename(target)
596+
except OSError:
597+
LOGGER.warn("Failed to preserve %s during update.", d)
598+
LOGGER.verbose("TRACEBACK", exc_info=True)
575599
else:
576-
dirs.extend(d for d in dirs[0].iterdir() if d.is_dir())
577-
dirs.pop(0)
600+
state.append((d, target))
578601
# Append None, target_root last to clean up after restore is done
579602
state.append((None, target_root))
580603
return state
@@ -634,7 +657,7 @@ def _install_one(cmd, source, install, *, target=None):
634657

635658
dest = target or (cmd.install_dir / install["id"])
636659

637-
preserved_site = _preserve_site(cmd, dest)
660+
preserved_site = _preserve_site(cmd, dest, install)
638661

639662
LOGGER.verbose("Extracting %s to %s", package, dest)
640663
if not cmd.repair:

0 commit comments

Comments
 (0)