Commits

Lynn Rees  committed cc04da9

- initial

  • Participants
  • Branches default

Comments (0)

Files changed (17)

+syntax:glob
+*.pyc
+*.egg-info
+*~
+.DS_Store
+.figleaf
+.coverage
+.project
+.pydevproject
+.git/
+.svn/
+.svn/*
+.settings/
+*.orig
+.tox/*
+venv/
+stuff/*
+syntax:regexp
+^build$
+^stuff$
+^dist$
+Copyright (c) 2012 L. C. Rees.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1.  Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+2.  Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3.  Neither the name of the Portable Site Information Project nor the names
+of its contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+include LICENSE.txt
+include MANIFEST.in
+include README.rst
+include setup.cfg
+include reqs/requires.txt
+include reqs/requires-2.6.txt
+recursive-include stage *.py

File README.rst

Empty file added.
+# -*- coding: utf-8 -*-
+'''stage fabfile'''
+
+from fabric.api import prompt, local, settings, env, lcd
+
+regup = './setup.py register sdist --format=gztar,zip upload'
+nodist = 'rm -rf ./dist'
+sphinxup = './setup.py upload_sphinx'
+
+
+def getversion(fname):
+    '''
+    Get the `__version__` without importing.
+    '''
+    for line in open(fname):
+        if line.startswith('__version__'):
+            return '%s.%s.%s' % eval(line[13:])
+
+
+def _promptup():
+    prompt('Enter tag: ', 'tag')
+    with settings(warn_only=True):
+        local('hg tag "%(tag)s"' % getversion('stage/__init__.py'))
+        local('hg push ssh://hg@bitbucket.org/lcrees/stage')
+        local('hg push github')
+
+
+def _test(val):
+    truth = val in ('py26', 'py27', 'py32')
+    if truth is False:
+        raise KeyError(val)
+    return val
+
+
+def tox():
+    '''test stage'''
+    local('tox')
+
+
+def docs():
+    with lcd('docs/'):
+        local('make clean')
+        local('make html')
+        local('make linkcheck')
+        local('make doctest')
+
+
+def update_docs():
+    docs()
+    with settings(warn_only=True):
+        local('hg ci -m docmerge')
+        local('hg push ssh://hg@bitbucket.org/lcrees/stage')
+        local('hg push github')
+    local(sphinxup)
+
+
+def tox_recreate():
+    '''recreate stage test env'''
+    prompt(
+        'Enter testenv: [py26, py27, py31, py32, pypy]',
+        'testenv',
+        validate=_test,
+    )
+    local('tox --recreate -e %(testenv)s' % env)
+
+
+def release():
+    '''release stage'''
+    docs()
+    local('hg update pu')
+    local('hg update next')
+    local('hg merge pu; hg ci -m automerge')
+    local('hg update maint')
+    local('hg merge default; hg ci -m automerge')
+    local('hg update default')
+    local('hg merge next; hg ci -m automerge')
+    local('hg update pu')
+    local('hg merge default; hg ci -m automerge')
+    _promptup()
+    local(regup)
+    local(sphinxup)
+    local(nodist)
+
+
+def releaser():
+    '''stage releaser'''
+    docs()
+    _promptup()
+    local(regup)
+    local(sphinxup)
+    local(nodist)
+
+
+def inplace():
+    '''in-place stage'''
+    docs()
+    with settings(warn_only=True):
+        local('hg push ssh://hg@bitbucket.org/lcrees/stage')
+        local('hg push github')
+    local('./setup.py sdist --format=gztar,zip upload')
+    local(sphinxup)
+    local(nodist)
+
+
+def release_next():
+    '''release stage from next branch'''
+    docs()
+    local('hg update maint')
+    local('hg merge default; hg ci -m automerge')
+    local('hg update default')
+    local('hg merge next; hg ci -m automerge')
+    local('hg update next')
+    local('hg merge default; hg ci -m automerge')
+    _promptup()
+    local(regup)
+    local(sphinxup)
+    local(nodist)

File reqs/dev_requires.txt

+distribute
+tox
+sphinx
+sphinxcontrib.spelling
+Sphinx-PyPI-upload
+Fabric
+hg-github

File reqs/docs_requires.txt

+sphinx
+distribute

File reqs/requires-2.6.txt

+ordereddict
+importlib
+parse>=1.4.1
+distribute
+stuf
+knife

File reqs/requires.txt

+stuf
+parse>=1.4.1
+knife

File reqs/test_requires.txt

+nose
+coverage
+knife
+stuf
+parse>=1.5.1
+[build_sphinx]
+source-dir = docs
+build-dir = docs/_build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = docs/_build/html
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+'''setup for stage'''
+
+import sys
+from os import getcwd
+from os.path import join
+from setuptools import setup, find_packages
+
+
+def getversion(fname):
+    '''Get __version__ without importing.'''
+    for line in open(fname):
+        if line.startswith('__version__'):
+            return '%s.%s.%s' % eval(line[13:].rstrip())
+
+if float('%d.%d' % sys.version_info[:2]) < 2.7:
+    reqs = 'reqs/requires-2.6.txt'
+else:
+    reqs = 'reqs/requires.txt'
+install_requires = list(l for l in open(join(getcwd(), reqs), 'r').readlines())
+
+setup(
+    name='stage',
+    version=getversion('stage/__init__.py'),
+    description='Pythonic configuration',
+    long_description=open(join(getcwd(), 'README.rst'), 'r').read(),
+    keywords='configuration settings management pythonic',
+    license='BSD',
+    author='L. C. Rees',
+    author_email='lcrees@gmail.com',
+    url='https://bitbucket.org/lcrees/stage',
+    packages=find_packages(),
+    test_suite='stage.tests',
+    zip_safe=False,
+    install_requires=install_requires,
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3.1',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: Implementation :: CPython',
+        'Topic :: Software Development',
+        'Topic :: Software Development :: Libraries',
+    ],
+)

File stage/__init__.py

+# -*- coding: utf-8 -*-
+'''Pythonic configuration'''
+
+__version__ = (0, 9, 5)

File stage/conf.py

+# -*- coding: utf-8 -*-
+'''Pythonic configuration'''
+
+from pprint import pformat
+from inspect import isclass
+
+from knife import lazyknife
+from stuf.utils import lazyload
+from stuf.desc import ResetMixin, lazy
+from stuf import exhaustmap, frozenstuf
+from stuf.six import intern, isstring, items
+
+SLOTS = '_all _these _this'.split()
+
+
+class _BaseFactory(object):
+
+    '''Base configuration factory.'''
+
+    def __repr__(self):  # pragma: no coverage
+        return pformat(self._all)
+
+    def __iter__(self):
+        return items(self._all)
+
+    def __enter__(self):
+        return self
+
+
+class ConfFactory(_BaseFactory):
+
+    '''Configuration factory.'''
+
+    def __init__(self):
+        super(ConfFactory, self).__init__()
+        self._all = {}
+        self._these = self._this = None
+
+    def __getattr__(self, key, getr=object.__getattribute__):
+        try:
+            return getr(self, key)
+        except AttributeError:
+            if not key.startswith('__'):
+                key = intern(key)
+                if self._these is None:
+                    self._all[key] = self._these = {}
+                elif self._this is None:
+                    self._this = key
+                return self
+
+    def __exit__(self, e, f, b):
+        self._these = self._this = None
+
+
+class DeepConf(_BaseFactory):
+
+    '''Deep (three level) configuration factory.'''
+
+    __slots__ = SLOTS + '_other _out'.split()
+
+    def __init__(self):
+        super(DeepConf, self).__init__()
+        # all configuration
+        self._all = {}
+        # everthing else
+        self._these = self._this = self._other = self._out = None
+
+    def __enter__(self):
+        self._these = {}
+        if self._out is None:
+            self._all[self._this] = self._out = self._these
+        self._other = self._this
+        return self
+
+    def __getattr__(self, key, getr=object.__getattribute__):
+        try:
+            return getr(self, key)
+        except AttributeError:
+            if not key.startswith('__'):
+                self._this = intern(key.upper())
+                return self
+
+    def __call__(self, *args):
+        if len(args) == 1:
+            first = args[0]
+            args = intern(first) if isstring(first) else first
+        else:
+            args = tuple(intern(a) for a in args if isstring(a))
+        self._these[self._this] = args
+        return self
+
+    def __exit__(self, e, f, b):
+        if self._these is not None:
+            if self._out != self._these:
+                self._out[self._other] = self._these
+            self._these = None
+        else:
+            self._out = None
+
+
+class FlatConf(ConfFactory):
+
+    '''Flatter (two level deep) configuration factory.'''
+
+    __slots__ = SLOTS
+
+    def __call__(self, *args):
+        self._these[self._this] = args[0] if len(args) == 1 else args
+        self._this = None
+        return self
+
+
+class Conf(ResetMixin):
+
+    '''Configuration manager.'''
+
+    __slots__ = '_defaults _required'.split()
+
+    def __init__(self, defaults=None, required=None):
+        '''
+        :keyword required: required settings
+        :keyword defaults: default settings
+        '''
+        super(Conf, self).__init__()
+        # configuration defaults
+        self._defaults = {}
+        self._load(self._defaults, defaults)
+        # required configuration
+        self._required = {}
+        self._load(self._required, required)
+
+    def __repr__(self):  # pragma: no coverage
+        return pformat(self.freeze())
+
+    def _load(self, destination, conf):
+        if isstring(conf):
+            conf = lazyload(conf)
+        if isclass(conf):
+            o = {}
+            t = iter(lazyknife(conf).traverse().attrs('maps').merge())
+            # setup base configuration
+            first = next(t)
+            first.pop('classname', None)
+            update = o.update
+            update(first)
+            # setup nested configuration
+            exhaustmap(lambda x: update({x.pop('classname', 'options'): x}), t)
+            destination.update(o)
+        elif isinstance(conf, _BaseFactory):
+            destination.update(conf._all)
+
+    @lazy
+    def defaults(self):
+        '''Get configuration default values.'''
+        return frozenstuf(self._defaults.copy())
+
+    @lazy
+    def required(self):
+        '''Get required configuration values.'''
+        return frozenstuf(self._required.copy())
+
+    def freeze(self, *args, **kw):
+        '''Finalize configuration values.'''
+        # 1. default options
+        end = self._defaults.copy()
+        # 2. final options
+        end.update(*args, **kw)
+        # 3. required options (last...they override everything)
+        end.update(self._required.copy())
+        return frozenstuf(end)

File tests/__init__.py

Empty file added.

File tests/test_stage.py

Empty file added.
+[tox]
+envlist = py26,py27,py31,py32,py33,pypy
+
+[testenv]
+deps=
+  -r{toxinidir}/reqs/test_requires.txt
+commands=
+  nosetests {posargs:--with-coverage --cover-package=stage}
+  
+[testenv:py31]
+deps=
+  unittest2py3k
+  -r{toxinidir}/reqs/test_requires.txt
+commands=
+  nosetests {posargs:--with-coverage  --cover-package=stage}
+
+[testenv:py26]
+deps=
+  unittest2
+  -r{toxinidir}/reqs/test_requires.txt
+commands=
+  nosetests {posargs:--with-coverage  --cover-package=stage}