2019-08-18 21:09:57 -04:00
|
|
|
"""
|
2022-05-05 00:32:20 +05:30
|
|
|
Manages automatic activation of virtual environments.
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
This coordinates multiple automatic vox policies and deals with some of the
|
|
|
|
mechanics of venv searching and chdir handling.
|
|
|
|
|
|
|
|
This provides no interface for end users.
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
Developers should look at XSH.builtins.events.autovox_policy
|
2019-08-18 21:09:57 -04:00
|
|
|
"""
|
|
|
|
import itertools
|
2022-01-31 21:26:34 +05:30
|
|
|
import warnings
|
2019-08-18 21:09:57 -04:00
|
|
|
from pathlib import Path
|
2022-01-31 21:26:34 +05:30
|
|
|
|
2019-08-18 21:09:57 -04:00
|
|
|
import xontrib.voxapi as voxapi
|
2022-05-30 15:33:17 +05:30
|
|
|
from xonsh.built_ins import XSH, XonshSession
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
__all__ = ()
|
|
|
|
|
|
|
|
|
2022-05-30 15:33:17 +05:30
|
|
|
def autovox_policy(path: "Path") -> "str|Path|None":
|
2019-08-18 21:09:57 -04:00
|
|
|
"""
|
2022-05-30 15:33:17 +05:30
|
|
|
Register a policy with autovox.
|
2019-08-18 21:09:57 -04:00
|
|
|
|
2022-05-30 15:33:17 +05:30
|
|
|
A policy is a function that takes a Path and returns the venv associated with it,
|
|
|
|
if any.
|
2019-08-18 21:09:57 -04:00
|
|
|
|
2022-05-30 15:33:17 +05:30
|
|
|
NOTE: The policy should only return a venv for this path exactly, not for
|
|
|
|
parent paths. Parent walking is handled by autovox so that all policies can
|
|
|
|
be queried at each level.
|
|
|
|
"""
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
class MultipleVenvsWarning(RuntimeWarning):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def get_venv(vox, dirpath):
|
|
|
|
# Search up the directory tree until a venv is found, or none
|
|
|
|
for path in itertools.chain((dirpath,), dirpath.parents):
|
|
|
|
venvs = [
|
|
|
|
vox[p]
|
2021-05-20 15:44:26 +05:30
|
|
|
for p in XSH.builtins.events.autovox_policy.fire(path=path)
|
2019-08-18 21:09:57 -04:00
|
|
|
if p is not None and p in vox # Filter out venvs that don't exist
|
|
|
|
]
|
|
|
|
if len(venvs) == 0:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
if len(venvs) > 1:
|
|
|
|
warnings.warn(
|
|
|
|
MultipleVenvsWarning(
|
2020-04-08 20:13:12 +02:00
|
|
|
"Found {numvenvs} venvs for {path}; using the first".format(
|
|
|
|
numvenvs=len(venvs), path=path
|
|
|
|
)
|
2019-08-18 21:09:57 -04:00
|
|
|
)
|
|
|
|
)
|
|
|
|
return venvs[0]
|
|
|
|
|
|
|
|
|
2020-02-03 13:25:06 -05:00
|
|
|
def check_for_new_venv(curdir, olddir):
|
2019-08-18 21:09:57 -04:00
|
|
|
vox = voxapi.Vox()
|
2020-02-03 13:25:06 -05:00
|
|
|
if olddir is ... or olddir is None:
|
|
|
|
try:
|
|
|
|
oldve = vox[...]
|
|
|
|
except KeyError:
|
|
|
|
oldve = None
|
|
|
|
else:
|
|
|
|
oldve = get_venv(vox, olddir)
|
2019-08-18 21:09:57 -04:00
|
|
|
newve = get_venv(vox, curdir)
|
|
|
|
|
|
|
|
if oldve != newve:
|
|
|
|
if newve is None:
|
|
|
|
vox.deactivate()
|
|
|
|
else:
|
2019-08-28 14:50:19 -04:00
|
|
|
vox.activate(newve.env)
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
# Core mechanism: Check for venv when the current directory changes
|
2022-05-30 15:33:17 +05:30
|
|
|
|
|
|
|
|
2020-02-03 13:25:06 -05:00
|
|
|
def cd_handler(newdir, olddir, **_):
|
|
|
|
check_for_new_venv(Path(newdir), Path(olddir))
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
# Recalculate when venvs are created or destroyed
|
|
|
|
|
|
|
|
|
|
|
|
def create_handler(**_):
|
2020-02-03 13:25:06 -05:00
|
|
|
check_for_new_venv(Path.cwd(), ...)
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
def destroy_handler(**_):
|
2020-02-03 13:25:06 -05:00
|
|
|
check_for_new_venv(Path.cwd(), ...)
|
2019-08-18 21:09:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
# Initial activation before first prompt
|
|
|
|
|
|
|
|
|
|
|
|
def load_handler(**_):
|
2020-02-03 13:25:06 -05:00
|
|
|
check_for_new_venv(Path.cwd(), None)
|
2022-05-30 15:33:17 +05:30
|
|
|
|
|
|
|
|
|
|
|
def _load_xontrib_(xsh: XonshSession, **_):
|
|
|
|
xsh.builtins.events.register(autovox_policy)
|
|
|
|
xsh.builtins.events.on_chdir(cd_handler)
|
|
|
|
xsh.builtins.events.vox_on_create(create_handler)
|
|
|
|
xsh.builtins.events.vox_on_destroy(destroy_handler)
|
|
|
|
xsh.builtins.events.on_post_init(load_handler)
|