227 lines
6.4 KiB
Python
227 lines
6.4 KiB
Python
import sys
|
|
from pathlib import Path
|
|
import stat
|
|
import subprocess
|
|
import logging
|
|
import json
|
|
import argparse
|
|
|
|
from .responses import compile_config
|
|
from .static import IS_VENV, VENV_DIRECTORY, CONFIG_DIRECTORY, COMPILED_CONFIG_FILE_NAME
|
|
|
|
logging.basicConfig(
|
|
format='%(message)s',
|
|
force=True,
|
|
)
|
|
|
|
log_level = logging.INFO
|
|
|
|
mommy_logger = logging.getLogger("mommy")
|
|
mommy_logger.setLevel(logging.INFO)
|
|
serious_logger = logging.getLogger("serious")
|
|
serious_logger.setLevel(50)
|
|
|
|
|
|
def config_logging(verbose: bool):
|
|
if verbose:
|
|
logging.basicConfig(
|
|
format=logging.BASIC_FORMAT,
|
|
force=True,
|
|
)
|
|
logging.getLogger().setLevel(logging.DEBUG)
|
|
mommy_logger.setLevel(50)
|
|
serious_logger.setLevel(logging.DEBUG)
|
|
|
|
|
|
WRAPPER_TEMPLATE = """#!{inner_bin}
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys, subprocess
|
|
from python_mommy_venv import get_response
|
|
|
|
|
|
INTERPRETER = "{inner_bin}"
|
|
result = subprocess.run([INTERPRETER] + sys.argv[1:])
|
|
code = result.returncode
|
|
|
|
print()
|
|
print(get_response(code))
|
|
exit(code=code)
|
|
"""
|
|
|
|
|
|
PIP_HOOK = """# GENERATED BY MOMMY
|
|
code = main()
|
|
|
|
from pathlib import Path
|
|
|
|
bin_path = Path(".venv", "bin")
|
|
|
|
python_interpreter_wrappers = []
|
|
for path in bin_path.iterdir():
|
|
if path.is_symlink() and path.name.startswith("inner_"):
|
|
python_interpreter_wrappers.append(path.name.replace("inner_", "", 1))
|
|
|
|
for path in bin_path.iterdir():
|
|
if path.is_symlink():
|
|
continue
|
|
if path.name in python_interpreter_wrappers:
|
|
continue
|
|
|
|
text: str
|
|
with path.open("r") as f:
|
|
text = f.read()
|
|
|
|
first_line = text.split("\\n")[0]
|
|
if not ("inner_" in first_line and first_line.startswith("#!")):
|
|
continue
|
|
|
|
print(f"mommifying " + str(path))
|
|
|
|
text = text.replace("inner_", "", 1)
|
|
with path.open("w") as f:
|
|
f.write(text)
|
|
|
|
sys.exit(code)"""
|
|
|
|
|
|
def assert_venv(only_warn: bool = False):
|
|
if not IS_VENV:
|
|
mommy_logger.error("mommy doesn't run in a virtual environment~")
|
|
serious_logger.error("this should run in a virtual environment")
|
|
if not only_warn:
|
|
exit(1)
|
|
|
|
|
|
def write_compile_config(local: bool):
|
|
assert_venv(only_warn=not local)
|
|
|
|
compiled_base_dir = VENV_DIRECTORY if local else CONFIG_DIRECTORY
|
|
compiled_config_file = compiled_base_dir / COMPILED_CONFIG_FILE_NAME
|
|
compiled = compile_config()
|
|
mommy_logger.info("mommy writes its moods in %s", compiled_config_file)
|
|
serious_logger.info("writing compiled config file to %s", compiled_config_file)
|
|
compiled_base_dir.mkdir(parents=True, exist_ok=True)
|
|
with compiled_config_file.open("w") as f:
|
|
json.dump(compiled, f, indent=4)
|
|
if not local:
|
|
(VENV_DIRECTORY / COMPILED_CONFIG_FILE_NAME).unlink(missing_ok=True)
|
|
|
|
|
|
def wrap_interpreter(path: Path):
|
|
mommy_logger.info("mommy found a symlink to an interpreter~ %s", str(path))
|
|
serious_logger.info("interpreter symlink found at %s", str(path))
|
|
|
|
inner_symlink = path.parent / ("inner_" + path.name)
|
|
symlink_target = path.resolve()
|
|
|
|
if inner_symlink.exists():
|
|
raise Exception("inner symlink somehow already exists. This shouldn't happen because of prior checks")
|
|
|
|
mommy_logger.info("mommy shows her girl where the interpreter is: %s -> %s", inner_symlink, symlink_target)
|
|
serious_logger.info("creating symlink: %s -> %s", inner_symlink, symlink_target)
|
|
inner_symlink.symlink_to(symlink_target)
|
|
|
|
# remove original symlink
|
|
mommy_logger.info("mommy deletes the original interpreter~ %s", path)
|
|
serious_logger.info("deleting original symlink %s", path)
|
|
path.unlink()
|
|
|
|
# creating the wrapper string
|
|
mommy_logger.info("mommy writes wrapper script as %s", Path)
|
|
serious_logger.info("writing wrapper script at %s", path)
|
|
with path.open("w") as f:
|
|
f.write(WRAPPER_TEMPLATE.format(inner_bin=str(inner_symlink)))
|
|
serious_logger.info("making wrapper script executable")
|
|
path.chmod(path.stat().st_mode | stat.S_IEXEC)
|
|
|
|
|
|
def install_pip_hook(path: Path):
|
|
text: str
|
|
with path.open("r") as f:
|
|
text = f.read()
|
|
|
|
if "# GENERATED BY MOMMY" in text:
|
|
mommy_logger.info("ahhhhh mommy already watches %s", str(path))
|
|
serious_logger.info("pip hook already installed at %s", str(path))
|
|
return
|
|
|
|
mommy_logger.info("mommy needs to keep an eye on this little pip~ %s", str(path))
|
|
serious_logger.info("installing pip hook at %s", str(path))
|
|
|
|
text = text.replace("sys.exit(main())", PIP_HOOK, 1)
|
|
with path.open("w") as f:
|
|
f.write(text)
|
|
|
|
|
|
def cli_compile_config():
|
|
parser = argparse.ArgumentParser(description="only recompile the config")
|
|
|
|
parser.add_argument(
|
|
"-v", "--verbose",
|
|
action="store_true",
|
|
help="enable verbose and serious output"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-l", "--local",
|
|
action="store_true",
|
|
help="compile the config only for the current virtual environment"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
write_compile_config(args.local)
|
|
|
|
|
|
def mommify_venv():
|
|
parser = argparse.ArgumentParser(description="patch the virtual environment to use mommy")
|
|
|
|
parser.add_argument(
|
|
"-v", "--verbose",
|
|
action="store_true",
|
|
help="enable verbose and serious output"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-l", "--local",
|
|
action="store_true",
|
|
help="compile the config only for the current virtual environment"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
config_logging(args.verbose)
|
|
assert_venv()
|
|
|
|
write_compile_config(args.local)
|
|
|
|
mommy_logger.info("")
|
|
|
|
bin_path = VENV_DIRECTORY / "bin"
|
|
bin_path = bin_path.resolve()
|
|
|
|
mommy_logger.info("mommy looks in %s to mess your system up~ <33", str(bin_path))
|
|
serious_logger.info("scanning binary directory of venv at %s", str(bin_path))
|
|
|
|
for path in list(bin_path.iterdir()):
|
|
name = path.name
|
|
|
|
if path.is_symlink():
|
|
# could be python interpreter
|
|
# check for both just to be more expressive
|
|
if name.startswith("inner_"):
|
|
continue
|
|
|
|
if subprocess.run([str(path), '-c', '"exit(0)"']).returncode != 0:
|
|
continue
|
|
|
|
wrap_interpreter(path)
|
|
|
|
else:
|
|
# could be pip
|
|
if not name.startswith("pip"):
|
|
continue
|
|
|
|
install_pip_hook(path)
|