Source code for freedom.profiles

# -*- coding: utf-8 -*-
"""
:summary: Profile and options handling.

:author: francis.horsman@gmail.com
"""

import os
import pprint
from abc import ABCMeta
from functools import wraps

import six

from .defaults import get_opts_key, OptionsKey, DEFAULT_ENV, DEFAULT_VERBOSE, \
    DEFAULT_PUBLISH_DOCS_PYPI, DEFAULT_PUBLISH_DOCS_RTD, \
    DEFAULT_PUBLISH_PYPI, DEFAULT_GIT_REPO, DEFAULT_GIT_PROFILE, \
    DEFAULT_BUILD_RELEASE, DEFAULT_BUILD_RELEASE_DOCS, DEFAULT_TAG_RELEASE, \
    DEFAULT_PUSH_RELEASE, DEFAULT_PYPIRC, DEFAULT_HISTORY_FILE, \
    DEFAULT_HISTORY_DELIMITER, DEFAULT_HISTORY_DATE, DEFAULT_HISTORY_VERSION, \
    DEFAULT_HISTORY_META, DEFAULT_HISTORY_FORMAT, DEFAULT_EXPORT_OPTIONS, \
    DEFAULT_BUILD_TEST, DEFAULT_PROJECT_NAME, DEFAULT_GITRC, \
    DEFAULT_GIT_PROPAGATE_ENV, DEFAULT_PYTHON_PROPAGATE_ENV, \
    DEFAULT_HISTORY_COMMENT, DEFAULT_INTERACTIVE_PROMPT, DEFAULT_INTERACTIVE, \
    DEFAULT_LOG_FILE, DEFAULT_LOG_LEVEL, DEFAULT_LOG_MODE
from .errors import NoProfileFile, ProfileLoadError, ProfileConfigurationError
from .utils import update_dict, iExporter


_SUPPORTED_PROFILES = dict()


class ProfileRegistry(type):
    def __new__(mcs, name, bases, dct):
        if name != '_Profile':
            attr_name = dct.get('NAME', None)
            if not bases == (object,) and not attr_name:
                raise ProfileConfigurationError(
                    '%s is not configured correctly, it must specify a'
                    'NAME class attribute.' % name)
            del dct['NAME']

            properties = dct.get('PROPERTIES')

            def _getter(key, default):

                @wraps(_getter)
                def func(self):
                    return self.get(key, default)

                return func

            for prop_options_key, prop_options_default in properties:
                prop_name = get_opts_key(prop_options_key)
                prop = property(_getter(prop_name, prop_options_default))
                dct[prop_name] = prop

            def get_args():
                @wraps(get_args)
                def _inner(**kwargs):
                    return {
                        get_opts_key(key): kwargs.get(get_opts_key(key),
                                                      default)
                        for key, default in properties}

                return _inner

            dct['get_args'] = staticmethod(get_args())

            del dct['PROPERTIES']

        cls = super(ProfileRegistry, mcs).__new__(mcs, name, bases, dct)
        if name != '_Profile':
            _SUPPORTED_PROFILES[attr_name] = cls
        return cls


@six.add_metaclass(ProfileRegistry)
@six.add_metaclass(ABCMeta)
class _Profile(dict):
    pass


class HistorianProfile(_Profile):
    NAME = 'history'
    PROPERTIES = [(OptionsKey.HISTORY_FILE, DEFAULT_HISTORY_FILE),
                  (OptionsKey.HISTORY_VERSION, DEFAULT_HISTORY_VERSION),
                  (OptionsKey.HISTORY_DELIMITER, DEFAULT_HISTORY_DELIMITER),
                  (OptionsKey.HISTORY_DATE, DEFAULT_HISTORY_DATE),
                  (OptionsKey.HISTORY_META, DEFAULT_HISTORY_META),
                  (OptionsKey.HISTORY_FORMAT, DEFAULT_HISTORY_FORMAT),
                  (OptionsKey.HISTORY_COMMENT, DEFAULT_HISTORY_COMMENT)]


class PublisherProfile(_Profile):
    NAME = 'publish'
    PROPERTIES = [
        (OptionsKey.PUBLISH_RELEASE_DOCS_PYPI, DEFAULT_PUBLISH_DOCS_PYPI),
        (OptionsKey.PUBLISH_RELEASE_DOCS_RTD, DEFAULT_PUBLISH_DOCS_RTD),
        (OptionsKey.PUBLISH_RELEASE_PYPI, DEFAULT_PUBLISH_PYPI)]


class BuilderProfile(_Profile):
    NAME = 'build'
    PROPERTIES = [(OptionsKey.BUILD_RELEASE_DOCS, DEFAULT_BUILD_RELEASE_DOCS),
                  (OptionsKey.BUILD_RELEASE, DEFAULT_BUILD_RELEASE),
                  (OptionsKey.BUILD_TEST, DEFAULT_BUILD_TEST),
                  (OptionsKey.GIT_PROPAGATE_ENV, DEFAULT_GIT_PROPAGATE_ENV),
                  (OptionsKey.PYTHON_PROPAGATE_ENV,
                   DEFAULT_PYTHON_PROPAGATE_ENV),
                  (OptionsKey.PYPIRC, DEFAULT_PYPIRC)]

    @property
    def home(self):
        return os.path.dirname(os.path.realpath(self.pypirc))


class VcsProfile(_Profile):
    NAME = 'vcs'
    PROPERTIES = [(OptionsKey.GIT_PROFILE, DEFAULT_GIT_PROFILE),
                  (OptionsKey.GITRC, DEFAULT_GITRC),
                  (OptionsKey.GIT_REPO, DEFAULT_GIT_REPO),
                  (OptionsKey.TAG_RELEASE, DEFAULT_TAG_RELEASE),
                  (OptionsKey.PUSH_RELEASE, DEFAULT_PUSH_RELEASE)]

    @property
    def home(self):
        return os.path.dirname(os.path.realpath(self.gitrc))


class MiscProfile(_Profile):
    NAME = 'misc'
    PROPERTIES = [(OptionsKey.DRY_RUN, DEFAULT_GIT_REPO),
                  (OptionsKey.PROJECT_NAME, DEFAULT_PROJECT_NAME),
                  (OptionsKey.INTERACTIVE, DEFAULT_INTERACTIVE),
                  (OptionsKey.INTERACTIVE_PROMPT, DEFAULT_INTERACTIVE_PROMPT),
                  (OptionsKey.GENERATE_RCFILE, DEFAULT_EXPORT_OPTIONS)]


class MonitorProfile(_Profile):
    NAME = 'monitor'
    PROPERTIES = [(OptionsKey.VERBOSE, DEFAULT_VERBOSE),
                  (OptionsKey.LOG_FILE, DEFAULT_LOG_FILE),
                  (OptionsKey.LOG_MODE, DEFAULT_LOG_MODE),
                  (OptionsKey.LOG_LEVEL, DEFAULT_LOG_LEVEL)]


[docs]class Profile(iExporter): """ Profile class to allow easy inspection of the profile sections. """ def __init__(self): self._profile_names = self._create_profiles() def _create_profiles(self): profiles_names = _SUPPORTED_PROFILES.keys() for name in profiles_names: cls = _SUPPORTED_PROFILES[name] setattr(self, name, cls()) return profiles_names def __str__(self): return 'Profile(%s): %s' % self.dry_run @property def _export_data(self): result = {} for name in self._profile_names: d = {} d.update(getattr(self, name)) result[name] = d return result def _update_data(self, d): """ Update (freshen) from exported_data """ for name in self._profile_names: d[name] = getattr(self, name).update(d.get(name, dict())) return self @staticmethod
[docs] def build(d): """ Build (overwrite) a new class from exported_data. """ return Profile()._update_data(d)
@staticmethod
[docs] def create(d): """ Create a new class from a single-depth dict (ie: options). """ result = {name: cls.get_args(**d) for name, cls in _SUPPORTED_PROFILES.iteritems()} return Profile()._update_data(result)
@property def dump(self): return pprint.pformat(self._export_data)
class NewProfile(object): @staticmethod def _validate_profile(d): return d @staticmethod def _load_profile(exporter, profile_path, silent=False): if not profile_path: return dict() if not os.path.exists(profile_path): if not silent: raise NoProfileFile( 'profile path does not exist: %s' % profile_path) return dict() try: return exporter.loads(open(profile_path), guess=True) except Exception as err: raise ProfileLoadError(err, 'Error parsing profile file (is it correct ' 'format?): %s' % profile_path) @staticmethod def _update_profile(profile, kwargs): return update_dict(profile, kwargs) @staticmethod def _load_profile_from_env(env): return os.environ.get(env, None) @staticmethod def _get_profile_file(profile, env=DEFAULT_ENV): # Load from env first: profile_ = NewProfile._load_profile_from_env(env) # Use cmd-line profile otherwise: return profile_ if profile_ is not None else profile @staticmethod def build(releaser, **kwargs): """ Factory method to do all the work of creating the profile dictionary from all provided sources. :param kwargs: kwargs to use / include in profile. :return: dict(profile) :ProfileLoadError - error in yaml loading (file exists but cannot be parsed). """ profile = kwargs.get('profile', None) profile_env = kwargs.get('ENV') profile_file = kwargs.get('profile', NewProfile._get_profile_file(profile, profile_env)) loaded_profile = NewProfile._load_profile( releaser.exporter, profile_file, silent=True) validated_profile = NewProfile._validate_profile(loaded_profile) updated_profile = NewProfile._update_profile(validated_profile, kwargs) return Profile.create(updated_profile) if __name__ == '__main__': # pragma no cover pass

Related Topics