diff --git a/README.md b/README.md index cdcf3aa2c348b547f1474ad27701fd6ddc85b452..60b8dab6f2411afc1b439140fdc340629adf6ea2 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,10 @@ to the same directory. ### Build and compress an environment using `cnd build` -Build the conda environment specified in `environment.yml` and -create a compressed squashfs image. +Build the conda environment specified in `environment.yml`. In case +a file `requirements.txt` is present, its contents will be installed +additionally using `pip`. Finally, create a compressed +squashfs image, and delete the files from staging the environment. ### Execute a command using `cnd exec` diff --git a/condainer/condainer.py b/condainer/condainer.py index 3a507c9bce6ab4057274345304630185938aff4a..e305231780edbc23657e5cef51abcd2d63a2e145 100644 --- a/condainer/condainer.py +++ b/condainer/condainer.py @@ -27,12 +27,13 @@ class termcol: def get_example_environment_yml(): raw = \ """ -name: basicnumpy +name: basic channels: - conda-forge dependencies: - python=3.9 - - numpy + - pip + #- numpy """ return yaml.safe_load(raw) @@ -174,18 +175,40 @@ def create_condainer_environment(cfg): exe = os.path.join(os.path.join(env_directory, 'bin'), cfg['conda_exe']) environment_yml = cfg["environment_yml"] cmd = f"{exe} env create --file {environment_yml} --name condainer".split() - proc = subprocess.Popen(cmd, shell=False) + env = copy.deepcopy(os.environ) + if "PYTHONPATH" in env: + del env["PYTHONPATH"] + proc = subprocess.Popen(cmd, shell=False, env=env) proc.communicate() assert(proc.returncode == 0) +def pip_condainer_environment(cfg): + """Install user-defined software stack (requirements.txt) into 'condainer' environment. + """ + env_directory = get_env_directory(cfg) + exe = os.path.join(os.path.join(env_directory, 'bin'), 'pip3') + requirements_txt = cfg["requirements_txt"] + if os.path.isfile(requirements_txt): + cmd = f"{exe} install --requirement {requirements_txt} --no-cache-dir".split() + env = copy.deepcopy(os.environ) + if "PYTHONPATH" in env: + del env["PYTHONPATH"] + proc = subprocess.Popen(cmd, shell=False, env=env) + proc.communicate() + assert(proc.returncode == 0) + + def clean_environment(cfg): """Delete pkg files and other unnecessary files from base environment. """ env_directory = get_env_directory(cfg) exe = os.path.join(os.path.join(env_directory, 'bin'), cfg['conda_exe']) cmd = f"{exe} clean --all --yes".split() - proc = subprocess.Popen(cmd, shell=False) + env = copy.deepcopy(os.environ) + if "PYTHONPATH" in env: + del env["PYTHONPATH"] + proc = subprocess.Popen(cmd, shell=False, env=env) proc.communicate() assert(proc.returncode == 0) @@ -227,6 +250,7 @@ def init(args): cfg = {} cfg['environment_yml'] = 'environment.yml' + cfg["requirements_txt"] = 'requirements.txt' cfg['installer_url'] = installer_url cfg['conda_exe'] = 'mamba' cfg['mount_base_directory'] = '/tmp' @@ -276,6 +300,7 @@ def build(args): os.makedirs(env_directory, exist_ok=True, mode=0o700) create_base_environment(cfg) create_condainer_environment(cfg) + pip_condainer_environment(cfg) clean_environment(cfg) compress_environment(cfg) write_activate_script(cfg)