# -*- coding: utf-8 -*-
import atexit
from collections import Mapping
from freckles.context.run_config import FrecklesRunConfig
from freckles.utils.runs import clean_runs_log_file
from .context.config import ContextConfigs
from .context.freckles_context import FrecklesContext
atexit.register(clean_runs_log_file)
# def show_cursor():
# cursor.show()
# atexit.register(show_cursor)
[docs]class Freckles(object):
"""The main class to encapsulate most things a developer would want to achieve with 'freckles'.
This has a fairly complex configuration processing mechanism in-built, which is mainly used to enable maximum
flexibility when building user-facing apps on top of 'freckles'. In most cases, you can safely ignore all that, and
set the 'config_repos' value to 'None', and use 'default_context_config' to set the context defaults you want to
use.
In case you want to utilize the config processing feature (but really, best to just ignore it, it is probably too
complex for what it does and will be re-written and simplified at some stage):
The 'default_context_config', can be either a string, a dict or a dict. If a list, every element will be merged
on-top of the previous (merged) dict. If a string, the 'config_repos' will be checked whether they contain a file
with that name and a '.context' file extension, if there is, the content of that file will be used, if not, the string
must either be 'default', in which case an empty dict will be returned, or it must contain a '=', in which case a
one-item dict will be created with the left side (before '=') used as key, and the right one as value (some simple
auto-parsing will be used to determine whether the value is a bool or integer).
Args:
context_config: the default configuration that underlies everything
extra_repos (list): a list of extra repositories to search for frecklets and resources (see above for format)
config_repos (dict, string): a single path or a dictionary of alias/path to point to folders containing context configurations
"""
def __init__(
self,
context_config=None,
extra_repos=None,
config_repos=None,
default_context_name="default",
):
self._context_configs = ContextConfigs.load_user_context_configs(
repos=config_repos
)
self._contexts = {}
self._current_context = None
self.create_context(
context_name=default_context_name,
context_config=context_config,
extra_repos=extra_repos,
)
[docs] def create_context(
self, context_name, context_config, set_current=False, extra_repos=None
):
if not context_name:
raise Exception("Context name can't be empty")
if context_name in self._contexts.keys():
raise Exception("Context '{}' already exists.".format((context_name)))
context_config = self._context_configs.create_context_config(
context_name=context_name,
config_chain=context_config,
extra_repos=extra_repos,
)
context = FrecklesContext(context_name=context_name, config=context_config)
self._contexts[context_name] = context
if set_current or self._current_context is None:
self._current_context = context_name
return context
@property
def current_context(self):
return self._contexts[self._current_context]
@current_context.setter
def current_context(self, context_name):
if context_name not in self._contexts.keys():
raise Exception("No context with name '{}' available.".format(context_name))
self._current_context = context_name
[docs] def get_context(self, context_name=None):
if context_name is None:
context_name = self._current_context
if context_name not in self._contexts.keys():
raise Exception("No context with name '{}' available.".format(context_name))
return self._contexts[context_name]
@property
def frecklets(self):
ctx = self.get_context()
return ctx.frecklet_index
[docs] def get_frecklet_index(self, context_name=None):
if context_name is None:
context_name = self._current_context
ctx = self.get_context(context_name)
return ctx.frecklet_index
[docs] def get_frecklet(self, frecklet_name, context_name=None):
ctx = self.get_context(context_name)
return ctx.get_frecklet(frecklet_name)
[docs] def create_frecklecutable(self, frecklet_name, context_name=None):
ctx = self.get_context(context_name)
return ctx.create_frecklecutable(frecklet_name)
[docs]class FrecklesDesc(object):
[docs] @classmethod
def from_dict(cls, context_config=None, extra_repos=None, context_alias="default"):
return FrecklesDesc(
context_config=context_config,
extra_repos=extra_repos,
context_alias=context_alias,
)
def __init__(self, context_config=None, extra_repos=None, context_alias=None):
self._context_config = context_config
self._extra_repos = extra_repos
if not context_alias:
context_alias = "default"
self._context_alias = context_alias
self._freckles_obj = None
self._context = None
[docs] def to_dict(self):
return {
"context_config": self.context_config,
"extra_repos": self.extra_repos,
"context_alias": self.context_alias,
}
@property
def context_config(self):
return self._context_config
@property
def extra_repos(self):
return self._extra_repos
@property
def context_alias(self):
return self._context_alias
@property
def freckles_obj(self):
if self._freckles_obj is not None:
return self._freckles_obj
freckles = Freckles(
context_config=self.context_config,
extra_repos=self.extra_repos,
default_context_name=self.context_alias,
)
self._context = freckles.get_context(self.context_alias)
[docs] def context(self):
if self._context is None:
self.freckles_obj
return self._context
[docs]class FrecklesRunDesc(object):
[docs] @classmethod
def from_dict(
cls,
frecklet_name,
vars=None,
run_config=None,
context_config=None,
extra_repos=None,
context_alias=None,
):
freckles_desc = FrecklesDesc(
context_config=context_config,
extra_repos=extra_repos,
context_alias=context_alias,
)
return FrecklesRunDesc(
frecklet_name=frecklet_name,
vars=vars,
run_config=run_config,
freckles_desc=freckles_desc,
)
def __init__(self, frecklet_name, vars=None, run_config=None, freckles_desc=None):
self._frecklet_name = frecklet_name
if vars is None:
vars = {}
self._vars = vars
self._run_config = FrecklesRunConfig.create(run_config)
if freckles_desc is None:
freckles_desc = FrecklesDesc()
elif isinstance(freckles_desc, Mapping):
freckles_desc = FrecklesDesc.from_dict(**freckles_desc)
self._freckles_desc = freckles_desc
@property
def frecklet_name(self):
return self._frecklet_name
@frecklet_name.setter
def frecklet_name(self, frecklet_name):
self._frecklet_name = frecklet_name
@property
def vars(self):
return self._vars
@vars.setter
def vars(self, vars):
if vars is None:
vars = {}
self._vars = vars
@property
def run_config(self):
return self._run_config
[docs] def to_dict(self):
return {
"frecklet_name": self._frecklet_name,
"vars": self._vars,
"run_config": self._run_config.config,
"freckles_desc": self._freckles_desc.to_dict(),
}
[docs] def run_frecklet(self, password_callback=None):
context = self._freckles_desc.context()
frecklet, _ = context.load_frecklet(self._frecklet_name)
fx = frecklet.create_frecklecutable(context=self._freckles_desc.context())
run_record = fx.run_frecklecutable(
inventory=self.vars,
run_config=self.run_config,
password_callback=password_callback,
)
return run_record