Source code for evariste.builder

# Copyright Louis Paternault 2015-2022
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Build process: gather files, and compile them.

.. autoclass:: Builder
   :members:
"""

# Can be removed starting with python3.11
from __future__ import annotations

import contextlib
import logging
import typing

from . import errors, plugins
from .cache import open_cache
from .hooks import setmethodhook
from .plugins import Loader
from .setup import Setup, SetupError
from .tree import Root

LOGGER = logging.getLogger("evariste")


[docs] class Builder(contextlib.AbstractContextManager): """Takes care of build process. Can be used as a context. .. attribute:: shared :type: evariste.shared.Shared Object that is shared and accessible by every :class:`evariste.plugins.Plugin` and :class:`evariste.tree.Tree`. See :ref:`shared`. .. attribute:: cache :type: evariste.cache.Cache Data that is cached between compilations. Plugin developpers won't manipulate this attribute directly (see :ref:`shared`). .. attribute:: plugins :type: evariste.plugins.Loader Plugin loader: loaded plugins are gathered there. """ # pylint: disable=no-member def __init__(self, setup): self.cache = open_cache(setup["setup"]["cachedir"], setup, self) self.shared = self.cache.shared self.plugins = Loader(shared=self.shared) LOGGER.info("Building directory tree…") try: vcs = list(self.plugins.values(plugin_type="vcs")) if len(vcs) != 1: raise errors.EvaristeError( # pylint: disable=consider-using-f-string "Exactly one vcs plugin must be enabled (right now: {}).".format( ", ".join([plugin.keyword for plugin in vcs]) ) ) self.tree = Root.from_vcs(vcs[0]) except plugins.NoMatch as error: raise SetupError( # pylint: disable=consider-using-f-string "Setup error: Value '{}' is not a valid vcs (available ones are: {}).".format( error.value, ", ".join([f"'{item}'" for item in error.available]), ) ) from error
[docs] @setmethodhook() def compile(self): """Compile files handled by this builder.""" LOGGER.info("Compiling…") self.tree.root_compile()
[docs] @setmethodhook() def close(self): """Perform close operations. Mainly used as a :ref:`methodhook`. """ self.cache.close()
[docs] @classmethod def from_setupname(cls, name: str) -> Builder: """Factory that returns a builder, given the name of a :ref:`setup file <setup>`.""" LOGGER.info("Reading setup…") return cls(Setup.from_file(name))
[docs] @classmethod def from_setupdict( cls, dictionary: typing.Dict[str, typing.Dict[str, str]] ) -> Builder: """Factory that returns a builder, given a setup dictionary. A *setup dictionary* is a dict that mimics :mod:`configparser` structure. """ LOGGER.info("Reading setup…") return cls(Setup(dictionary))
def __enter__(self, *args, **kwargs): # pylint: disable=useless-super-delegation return super().__enter__(*args, **kwargs) def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: self.close() return super().__exit__(exc_type, exc_value, traceback)