-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
virtualenv
Overhaul of almost the entire package, with the following main changes: 1. Add a `ProjectConfig` class based on `pydantic`'s `BaseSettings`. It uses `python-dotenv` to store the configuration in the `$HOME/.aiida_project.env` file. In order to provide integration with `virtualenvwrapper`, the default virtual Python environment directory is set `WORKON_HOME` if this variable has been set in the UNIX environment. 2. Add a `ProjectDict` configuration with the goal of providing one interface for loading and storing the information regarding the created projects. 3. The "engines" have been rebranded as "projects", since they do not only create Python virtual environments, but also manage the "project" directory, where e.g. the `.aiida` folder is located. The separation of the Python environment directory and the `.aiida` one is intentional; it allow the user to e.g. complete remove and reset the Python environment easily if needed. 4. Redesign the rebranded `BaseProject` class as a `pydantic.BaseModel`. The main idea is that we want to have a clear syntax with type hinting for the generation of `BaseProject` instances, that can easily be stored and parsed from files. 5. Add the `VirtualenvProject` class to replace `VirtualenvEngine`. It has the basic methods needed for creating the environment, adding lines to the `activate` and `deactivate` scripts created by `virtualenv`, and allows to both install packages from PyPI as well as a local folder. 6. The rebranded `CondaProject` is disabled for now, with the promise of extending support for conda environments in a future commit. 7. The `aiida-project create` CLI command is rewritten to accommodate these changes.
- Loading branch information
Showing
12 changed files
with
244 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from os import environ | ||
from pathlib import Path | ||
from typing import Dict, Optional, Union | ||
|
||
import dotenv | ||
from pydantic import BaseSettings | ||
|
||
from .project import EngineType | ||
from .project.base import BaseProject | ||
|
||
|
||
class ProjectConfig(BaseSettings): | ||
"""Configuration class for configuring `aiida-project`.""" | ||
|
||
aiida_venv_dir: Path = Path(Path.home(), ".aiida_venvs") | ||
aiida_project_dir: Path = Path(Path.home(), "project") | ||
default_python_path: Optional[Path] = None | ||
|
||
class Config: | ||
env_file = Path.home() / Path(".aiida_project.env") | ||
env_file_encoding = "utf-8" | ||
|
||
def __init__(self, **configuration): | ||
super().__init__(**configuration) | ||
if dotenv.get_key(self.Config.env_file, "aiida_venv_dir") is None: | ||
dotenv.set_key( | ||
self.Config.env_file, | ||
"aiida_venv_dir", | ||
environ.get("WORKON_HOME", self.aiida_venv_dir.as_posix()), | ||
) | ||
|
||
|
||
class ProjectDict: | ||
_projects_path = Path(ProjectConfig().aiida_project_dir, ".aiida_projects") | ||
|
||
def __init__(self): | ||
if not self._projects_path.exists(): | ||
self._projects_path.joinpath("virtualenv").mkdir(parents=True, exist_ok=True) | ||
self._projects_path.joinpath("conda").mkdir(parents=True, exist_ok=True) | ||
|
||
@property | ||
def projects(self) -> Dict[str, BaseProject]: | ||
projects = {} | ||
for project_file in self._projects_path.glob("**/*.json"): | ||
engine = EngineType[str(project_file.parent.name)].value | ||
project = engine.parse_file(project_file) | ||
projects[project.name] = project | ||
return projects | ||
|
||
def add_project(self, project: BaseProject) -> None: | ||
"""Add a project to the configuration files.""" | ||
with Path(self._projects_path, project.engine, f"{project.name}.json").open("w") as handle: | ||
handle.write(project.json()) | ||
|
||
def remove_project(self, project: Union[str, BaseProject]) -> None: | ||
"""Remove a project from the configuration files.""" | ||
project = self.projects[project] if isinstance(project, str) else project | ||
Path(self._projects_path, project.engine, f"{project.name}.json").unlink() |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from enum import Enum | ||
from pathlib import Path | ||
|
||
from .base import BaseProject | ||
from .conda import CondaProject | ||
from .virtualenv import VirtualenvProject | ||
|
||
|
||
class EngineType(Enum): | ||
virtualenv = VirtualenvProject | ||
conda = CondaProject | ||
|
||
|
||
def get_project(engine: str, name: str, project_path: Path, venv_path: Path) -> BaseProject: | ||
"""Get a ``BaseProject`` instance using the corresponding environment engine.""" | ||
|
||
return EngineType[engine].value(name=name, project_path=project_path, venv_path=venv_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from abc import ABC, abstractmethod | ||
from pathlib import Path | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
class BaseProject(BaseModel, ABC): | ||
name: str | ||
project_path: Path | ||
venv_path: Path | ||
|
||
_engine = "" | ||
|
||
@property | ||
def engine(self): | ||
return self._engine | ||
|
||
@abstractmethod | ||
def create(self, python_path=None): | ||
"""Create the project.""" | ||
Path(self.project_path, ".aiida").mkdir(parents=True, exist_ok=True) | ||
|
||
@abstractmethod | ||
def append_activate_text(self, text: str) -> None: | ||
"""Append a text to the activate script.""" | ||
|
||
@abstractmethod | ||
def append_deactivate_text(self, text: str) -> None: | ||
"""Append a text to the deactivate script.""" | ||
|
||
@abstractmethod | ||
def install(self, package): | ||
"""Install a package from PyPI.""" | ||
|
||
@abstractmethod | ||
def install_local(self, path): | ||
"""Install a package from a local directory.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# import json | ||
# import os | ||
# import subprocess | ||
|
||
# import py # type: ignore | ||
|
||
from aiida_project.project.base import BaseProject | ||
|
||
|
||
class CondaProject(BaseProject): | ||
"""Conda environment.""" | ||
|
||
_engine = "conda" | ||
|
||
|
||
# def __init__(self, project_path): | ||
# super().__init__(project_path) | ||
# self.conda_info = json.loads( | ||
# subprocess.check_output(["conda", "info", "-a", "--json"]) | ||
# ) | ||
|
||
# @property | ||
# def conda_path(self): | ||
# return py.path.local(self.conda_info["conda_prefix"]) | ||
|
||
# @property | ||
# def venv_path(self): | ||
# for path in self.conda_info["envs_dirs"]: | ||
# env_collection_path = py.path.local(path) | ||
# env_path = env_collection_path.join(self.venv_name) | ||
# if env_path.exists(): | ||
# return env_path | ||
# return None | ||
|
||
# def create(self, name, python=None): | ||
# self._venv_name = name | ||
# if not self.venv_path: | ||
# conda_command = ["conda", "create", "-y", "-n", name, "python=2"] | ||
# subprocess.check_call(conda_command) | ||
|
||
# def add_activate_script(self, path): | ||
# activate_d_path = self.venv_path.join("etc", "conda", "activate.d") | ||
# activate_d_path.ensure_dir() | ||
# os.symlink(path.strpath, activate_d_path.join(path.basename).strpath) | ||
|
||
# def add_deactivate_scritp(self, path): | ||
# deactivate_d_path = self.venv_path.join("etc", "conda", "deactivate.d") | ||
# deactivate_d_path.ensure_dir() | ||
# os.symlink(path.strpath, deactivate_d_path.join(path.basename).strpath) |
Oops, something went wrong.