mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-12-22 10:40:44 -06:00
refactor: more classes less funk
This commit is contained in:
parent
2864f50176
commit
88983d288b
2 changed files with 63 additions and 59 deletions
2
Makefile
2
Makefile
|
@ -20,7 +20,7 @@ venv: ## generate environment
|
||||||
|
|
||||||
.PHONY: dev-install
|
.PHONY: dev-install
|
||||||
dev-install:
|
dev-install:
|
||||||
cp ./src/viv/viv.py ~/.local/share/viv/viv.py
|
ln -sf $(PWD)/srv/viv/viv.py ~/.local/share/viv/viv.py
|
||||||
|
|
||||||
docs: docs/index.md docs/viv.py ## build docs
|
docs: docs/index.md docs/viv.py ## build docs
|
||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
120
src/viv/viv.py
120
src/viv/viv.py
|
@ -50,7 +50,7 @@ from typing import (
|
||||||
from urllib.error import HTTPError
|
from urllib.error import HTTPError
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
__version__ = "23.5a5-4-g6e173b2-dev"
|
__version__ = "23.5a5-5-g2864f50-dev"
|
||||||
|
|
||||||
|
|
||||||
class Spinner:
|
class Spinner:
|
||||||
|
@ -110,6 +110,34 @@ class Spinner:
|
||||||
sys.stderr.write("\r")
|
sys.stderr.write("\r")
|
||||||
|
|
||||||
|
|
||||||
|
class Env:
|
||||||
|
defaults = dict(
|
||||||
|
viv_bin_dir=Path.home() / ".local" / "bin",
|
||||||
|
xdg_cache_home=Path.home() / ".cache",
|
||||||
|
xdg_data_home=Path.home() / ".local" / "share",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if not attr.startswith("_") and (defined := getattr(self, f"_{attr}")):
|
||||||
|
return defined
|
||||||
|
else:
|
||||||
|
return os.getenv(attr.upper(), self.defaults.get(attr))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _viv_cache(self) -> Path:
|
||||||
|
return os.getenv("VIV_CACHE") or (Path(self.xdg_cache_home) / "viv")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _viv_spec(self) -> List[str]:
|
||||||
|
return filter(None, os.getenv("VIV_SPEC", "").split(" "))
|
||||||
|
|
||||||
|
|
||||||
|
class Cfg:
|
||||||
|
@property
|
||||||
|
def src(self) -> Path:
|
||||||
|
return Path(Env().xdg_data_home) / "viv" / "viv.py"
|
||||||
|
|
||||||
|
|
||||||
class Ansi:
|
class Ansi:
|
||||||
"""control ouptut of ansi(VT100) control codes"""
|
"""control ouptut of ansi(VT100) control codes"""
|
||||||
|
|
||||||
|
@ -129,7 +157,7 @@ class Ansi:
|
||||||
self.option: str = self.yellow
|
self.option: str = self.yellow
|
||||||
self.metavar: str = "\033[33m" # normal yellow
|
self.metavar: str = "\033[33m" # normal yellow
|
||||||
|
|
||||||
if os.getenv("NO_COLOR") or not sys.stderr.isatty():
|
if Env().no_color or not sys.stderr.isatty():
|
||||||
for attr in self.__dict__:
|
for attr in self.__dict__:
|
||||||
setattr(self, attr, "")
|
setattr(self, attr, "")
|
||||||
|
|
||||||
|
@ -352,7 +380,7 @@ if __name__ == "__main__":
|
||||||
("CLI", cli),
|
("CLI", cli),
|
||||||
("Running Source", running),
|
("Running Source", running),
|
||||||
("Local Source", local),
|
("Local Source", local),
|
||||||
("Cache", Cfg._cache),
|
("Cache", Cache().base),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
+ "\n"
|
+ "\n"
|
||||||
|
@ -617,18 +645,18 @@ class Meta:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, name: str) -> "Meta":
|
def load(cls, name: str) -> "Meta":
|
||||||
if not (Cfg.venvcache / "vivmeta.json").exists():
|
if not (Cache().venv / name / "vivmeta.json").exists():
|
||||||
warn(f"possibly corrupted vivenv: {name}")
|
warn(f"possibly corrupted vivenv: {name}")
|
||||||
# add empty values for corrupted vivenvs so it will still load
|
# add empty values for corrupted vivenvs so it will still load
|
||||||
return cls(name=name, spec=[""], files=[""], exe="", id="")
|
return cls(name=name, spec=[""], files=[""], exe="", id="")
|
||||||
else:
|
else:
|
||||||
meta = json.loads((c.venvcache / name / "vivmeta.json").read_text())
|
meta = json.loads((Cache().venv / name / "vivmeta.json").read_text())
|
||||||
|
|
||||||
return cls(**meta)
|
return cls(**meta)
|
||||||
|
|
||||||
def write(self, p: Path | None = None) -> None:
|
def write(self, p: Path | None = None) -> None:
|
||||||
if not p:
|
if not p:
|
||||||
p = (c.venvcache) / self.name / "vivmeta.json"
|
p = (Cache().venv) / self.name / "vivmeta.json"
|
||||||
|
|
||||||
p.write_text(json.dumps(self.__dict__))
|
p.write_text(json.dumps(self.__dict__))
|
||||||
|
|
||||||
|
@ -653,10 +681,10 @@ class ViVenv:
|
||||||
id = id if id else get_hash(spec, track_exe)
|
id = id if id else get_hash(spec, track_exe)
|
||||||
|
|
||||||
self.name = name if name else id
|
self.name = name if name else id
|
||||||
self.path = path if path else c.venvcache / self.name
|
self.path = path if path else Cache().venv / self.name
|
||||||
|
|
||||||
if not metadata:
|
if not metadata:
|
||||||
if self.name in (d.name for d in c.venvcache.iterdir()):
|
if self.name in (d.name for d in Cache().venv.iterdir()):
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
self.meta = Meta.load(self.name)
|
self.meta = Meta.load(self.name)
|
||||||
else:
|
else:
|
||||||
|
@ -713,7 +741,7 @@ class ViVenv:
|
||||||
cmd,
|
cmd,
|
||||||
spinmsg="installing packages in vivenv",
|
spinmsg="installing packages in vivenv",
|
||||||
clean_up_path=self.path,
|
clean_up_path=self.path,
|
||||||
verbose=bool(os.getenv("VIV_VERBOSE")),
|
verbose=bool(Env().viv_verbose),
|
||||||
)
|
)
|
||||||
|
|
||||||
def touch(self) -> None:
|
def touch(self) -> None:
|
||||||
|
@ -739,7 +767,7 @@ class ViVenv:
|
||||||
|
|
||||||
def tree(self) -> None:
|
def tree(self) -> None:
|
||||||
_id = self.meta.id if self.meta.id == self.name else self.name
|
_id = self.meta.id if self.meta.id == self.name else self.name
|
||||||
# TODO: generarlize and loop this or make a template..
|
|
||||||
items = [
|
items = [
|
||||||
f"{a.magenta}{k}{a.end}: {v}"
|
f"{a.magenta}{k}{a.end}: {v}"
|
||||||
for k, v in {
|
for k, v in {
|
||||||
|
@ -778,8 +806,8 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
|
||||||
name: use as vivenv name, if not provided id is used
|
name: use as vivenv name, if not provided id is used
|
||||||
"""
|
"""
|
||||||
|
|
||||||
vivenv = ViVenv([*list(packages), *c.viv_spec], track_exe=track_exe, name=name)
|
vivenv = ViVenv([*list(packages), *Env().viv_spec], track_exe=track_exe, name=name)
|
||||||
if not vivenv.loaded or os.getenv("VIV_FORCE"):
|
if not vivenv.loaded or Env().viv_force:
|
||||||
vivenv.create()
|
vivenv.create()
|
||||||
vivenv.install_pkgs()
|
vivenv.install_pkgs()
|
||||||
|
|
||||||
|
@ -797,7 +825,7 @@ def modify_sys_path(new_path: Path) -> None:
|
||||||
|
|
||||||
def get_venvs() -> Dict[str, ViVenv]:
|
def get_venvs() -> Dict[str, ViVenv]:
|
||||||
vivenvs = {}
|
vivenvs = {}
|
||||||
for p in c.venvcache.iterdir():
|
for p in Cache().venv.iterdir():
|
||||||
vivenv = ViVenv.load(p.name)
|
vivenv = ViVenv.load(p.name)
|
||||||
vivenvs[vivenv.name] = vivenv
|
vivenvs[vivenv.name] = vivenv
|
||||||
return vivenvs
|
return vivenvs
|
||||||
|
@ -856,7 +884,7 @@ def fetch_source(reference: str) -> str:
|
||||||
(hash := hashlib.sha256()).update(src.encode())
|
(hash := hashlib.sha256()).update(src.encode())
|
||||||
sha256 = hash.hexdigest()
|
sha256 = hash.hexdigest()
|
||||||
|
|
||||||
cached_src_file = c.srccache / f"{sha256}.py"
|
cached_src_file = Cache().src / f"{sha256}.py"
|
||||||
|
|
||||||
if not cached_src_file.is_file():
|
if not cached_src_file.is_file():
|
||||||
cached_src_file.write_text(src)
|
cached_src_file.write_text(src)
|
||||||
|
@ -871,23 +899,13 @@ def make_executable(path: Path) -> None:
|
||||||
os.chmod(path, mode)
|
os.chmod(path, mode)
|
||||||
|
|
||||||
|
|
||||||
class Cfg:
|
def ensure(d: Path) -> Path:
|
||||||
"""viv config manager"""
|
d.mkdir(exist_ok=True, parents=True)
|
||||||
|
return d
|
||||||
|
|
||||||
_cache = Path(
|
|
||||||
os.getenv(
|
|
||||||
"VIV_CACHE",
|
|
||||||
Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
class Cache:
|
||||||
def viv_spec(self) -> List[str]:
|
base = Env().viv_cache
|
||||||
env_viv_spec = os.getenv("VIV_SPEC")
|
|
||||||
if env_viv_spec:
|
|
||||||
return env_viv_spec.split(" ")
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ensure(p: Path) -> Path:
|
def _ensure(p: Path) -> Path:
|
||||||
|
@ -895,28 +913,12 @@ class Cfg:
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def venvcache(self) -> Path:
|
def src(self) -> Path:
|
||||||
return self._ensure(self._cache / "venvs")
|
return self._ensure(self.base / "src")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def srccache(self) -> Path:
|
def venv(self) -> Path:
|
||||||
return self._ensure(self._cache / "src")
|
return self._ensure(self.base / "venvs")
|
||||||
|
|
||||||
@property
|
|
||||||
def binparent(self) -> Path:
|
|
||||||
return self._ensure(
|
|
||||||
Path(os.getenv("VIV_BIN_DIR", Path.home() / ".local" / "bin"))
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def srcdefault(self) -> Path:
|
|
||||||
parent = (
|
|
||||||
Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local" / "share")) / "viv"
|
|
||||||
)
|
|
||||||
return self._ensure(parent) / "viv.py"
|
|
||||||
|
|
||||||
|
|
||||||
c = Cfg()
|
|
||||||
|
|
||||||
|
|
||||||
class Viv:
|
class Viv:
|
||||||
|
@ -994,7 +996,7 @@ class Viv:
|
||||||
# re-create env again since path's are hard-coded
|
# re-create env again since path's are hard-coded
|
||||||
vivenv = ViVenv(spec)
|
vivenv = ViVenv(spec)
|
||||||
|
|
||||||
if not vivenv.loaded or os.getenv("VIV_FORCE"):
|
if not vivenv.loaded or Env().viv_force:
|
||||||
vivenv.create()
|
vivenv.create()
|
||||||
vivenv.install_pkgs()
|
vivenv.install_pkgs()
|
||||||
vivenv.meta.write()
|
vivenv.meta.write()
|
||||||
|
@ -1066,7 +1068,7 @@ class Viv:
|
||||||
|
|
||||||
def _install_local_src(self, sha256: str, src: Path, cli: Path, yes: bool) -> None:
|
def _install_local_src(self, sha256: str, src: Path, cli: Path, yes: bool) -> None:
|
||||||
echo("updating local source copy of viv")
|
echo("updating local source copy of viv")
|
||||||
shutil.copy(Cfg.srccache / f"{sha256}.py", src)
|
shutil.copy(Cache.src / f"{sha256}.py", src)
|
||||||
make_executable(src)
|
make_executable(src)
|
||||||
echo("symlinking cli")
|
echo("symlinking cli")
|
||||||
|
|
||||||
|
@ -1085,7 +1087,7 @@ class Viv:
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_new_version(self, ref: str) -> Tuple[str, str]:
|
def _get_new_version(self, ref: str) -> Tuple[str, str]:
|
||||||
sys.path.append(str(Cfg.srccache))
|
sys.path.append(str(Cache.src))
|
||||||
return (sha256 := fetch_source(ref)), __import__(sha256).__version__
|
return (sha256 := fetch_source(ref)), __import__(sha256).__version__
|
||||||
|
|
||||||
def manage(self, args: Namespace) -> None:
|
def manage(self, args: Namespace) -> None:
|
||||||
|
@ -1145,11 +1147,11 @@ class Viv:
|
||||||
|
|
||||||
elif args.subcmd == "purge":
|
elif args.subcmd == "purge":
|
||||||
to_remove = []
|
to_remove = []
|
||||||
if Cfg._cache.is_dir():
|
if Cache.base.is_dir():
|
||||||
to_remove.append(Cfg._cache)
|
to_remove.append(Cache.base)
|
||||||
if args.src.is_file():
|
if args.src.is_file():
|
||||||
to_remove.append(
|
to_remove.append(
|
||||||
args.src.parent if args.src == Cfg.srcdefault else args.src
|
args.src.parent if args.src == (Cfg().src) else args.src
|
||||||
)
|
)
|
||||||
if self.local_source and self.local_source.is_file():
|
if self.local_source and self.local_source.is_file():
|
||||||
if self.local_source.parent.name == "viv":
|
if self.local_source.parent.name == "viv":
|
||||||
|
@ -1191,7 +1193,9 @@ class Viv:
|
||||||
"""
|
"""
|
||||||
default_bin, bin = self._pick_bin(args)
|
default_bin, bin = self._pick_bin(args)
|
||||||
output = (
|
output = (
|
||||||
Cfg.binparent / default_bin if not args.output else args.output.absolute()
|
Env().viv_bin_dir / default_bin
|
||||||
|
if not args.output
|
||||||
|
else args.output.absolute()
|
||||||
)
|
)
|
||||||
|
|
||||||
if output.is_file():
|
if output.is_file():
|
||||||
|
@ -1255,7 +1259,7 @@ class Viv:
|
||||||
# ephemeral (default), semi-ephemeral (persist inside /tmp), or
|
# ephemeral (default), semi-ephemeral (persist inside /tmp), or
|
||||||
# persist (use c.cache)
|
# persist (use c.cache)
|
||||||
|
|
||||||
if not vivenv.loaded or os.getenv("VIV_FORCE"):
|
if not vivenv.loaded or Env().viv_force:
|
||||||
if not args.keep:
|
if not args.keep:
|
||||||
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
||||||
vivenv.path = Path(tmpdir)
|
vivenv.path = Path(tmpdir)
|
||||||
|
@ -1370,7 +1374,7 @@ class Cli:
|
||||||
"-s",
|
"-s",
|
||||||
"--src",
|
"--src",
|
||||||
help="path/to/source_file",
|
help="path/to/source_file",
|
||||||
default=Cfg.srcdefault,
|
default=Cfg().src,
|
||||||
metavar="<src>",
|
metavar="<src>",
|
||||||
),
|
),
|
||||||
Arg(
|
Arg(
|
||||||
|
|
Loading…
Reference in a new issue