<commit_before>#!/usr/bin/env python3

import time
import threading
import queue
import weakref
from collections import defaultdict
from electroncash.util import PrintError

class ExpiringCache:
    ''' A fast cache useful for storing tens of thousands of lightweight items.

    Use this class to cache the results of functions or other computations
    when:

        1. Many identical items are repetitively created (or many duplicate
           computations are repetitively performed) during normal app
           execution, and it makes sense to cache them.
        2. The creation of said items is more computationally expensive than
           accessing this cache.
        3. The memory tradeoff is acceptable. (As with all caches, you are
           trading CPU cost for memory cost).

    And example of this is UI code or string formatting code that refreshes the
    display with (mostly) the same output over and over again. In that case it
    may make more sense to just cache the output items (such as the formatted
    amount results from format_satoshis), rather than regenerate them, as a
    performance tweak.

    ExpiringCache automatically has old items expire if `maxlen' is exceeded.

    Items are timestamped with a 'tick count' (granularity of 10 seconds per
    tick). Their timestamp is updated each time they are accessed via `get' (so
    that only the oldest items that are least useful are the first to expire on
    cache overflow).

    get() and put() are fast. A background thread is used to safely
    expire items when the cache overflows (so that get and put never stall
    to manage the cache's size and/or to flush old items).  This background
    thread runs every 10 seconds -- so caches may temporarily overflow past
    their maxlen for up to 10 seconds. '''
    def __init__(self, *, maxlen=10000, name="An Unnamed Cache"):
        assert maxlen > 0
        self.maxlen = maxlen
        self.name = name
        self.d = dict()
        _ExpiringCacheMgr.add_cache(self)
    def get(self, key, default=None):
        res = self.d.get(key)
        if res is not None:
            # cache hit
            res[0] = _ExpiringCacheMgr.tick  # update tick access time for this cache hit
            return res[1]
        # cache miss
        return default
    def put(self, key, value):
        self.d[key] = [_ExpiringCacheMgr.tick, value]
    def size_bytes(self):
        ''' Returns the cache's memory usage in bytes. This is done by doing a
        deep, recursive examination of the cache contents. '''
        return get_object_size(
            self.d.copy()  # prevent iterating over a mutating dict.
        )
    def __len__(self):
        return len(self.d)

class _ExpiringCacheMgr(PrintError):
    '''Do not use this class directly. Instead just create ExpiringCache
    instances and that will handle the creation of this object automatically
    and its lifecycle.

    This is a singleton that manages the ExpiringCaches. It creates a thread
    that wakes up every tick_interval seconds and expires old items from
    overflowing extant caches.

    Note that after the last cache is gc'd the manager thread will exit and
    this singleton object also will expire and clean itself up automatically.'''
    _lock = threading.Lock()  # used to lock _instance and self.caches
    _instance = None
    tick = 0
    tick_interval = 10.0  # seconds; we wake up this often to update 'tick' and also to expire old items for overflowing caches

    def __init__(self, add_iter=None):
        cls = type(self)
        assert not cls._instance, "_ExpiringCacheMgr is a singleton"
        super().__init__()
        cls._instance = self
        self.q = queue.Queue()
        self.caches = weakref.WeakSet()
        if add_iter:
            self.caches.update(add_iter)
        self.livect = len(self.caches)  # this is updated by add_cache and on_cache_gc below.
        self.thread = threading.Thread(target=self.mgr_thread, daemon=True)
        self.thread.start()

    @classmethod
    def add_cache(cls, *caches):
        assert caches
        new_caches = caches
        with cls._lock:
            slf = cls._instance
            if not slf:
                slf = cls(caches)
                assert slf == cls._instance
            else:
                new_caches = [c for c in caches if c not in slf.caches]
                slf.caches.update(new_caches)
            for cache in new_caches:
                # add finalizer for each new cache
                weakref.finalize(cache, cls.on_cache_gc, cache.name)
            slf.livect = len(slf.caches)

    @classmethod
    def on_cache_gc(cls, name):
        assert cls._instance
        thread2join = None
        with cls._lock:
            slf = cls._instance
            assert slf.thread.is_alive()
            slf.livect -= 1 # we need to keep this counter because the weak set doesn't have the correct length at this point yet.
            slf.print_error("Cache '{}' has been gc'd, {} still alive".format(name, slf.livect))
            if not slf.livect:  # all caches have been gc'd, kill the thread
                slf.print_error("No more caches, stopping manager thread and removing singleton")
                slf.q.put(None)  # signal thread to stop
                thread2join = slf.thread
                cls._instance = None  # kill self.
        if thread2join:
            # we do this here as defensive programming to avoid deadlocks in case
            # thread ends up taking locks in some future implementation.
            thread2join.join()

    def mgr_thread(self):
        cls = type(self)
        self.print_error("thread started")
        try:
            while True:
                try:
                    x = self.q.get(timeout=self.tick_interval)
                    return # we got a stop signal
                except queue.Empty:
                    # normal condition, we slept with nothing to do
                    pass
                cls.tick += 1
                for c in tuple(self.caches):  # prevent cache from dying while we iterate
                    len_c = len(c.d)  # capture length here as c.d may mutate and grow while this code executes.
                    if len_c > c.maxlen:
                        t0 = time.time()
                        num = cls._try_to_expire_old_items(c.d, len_c - c.maxlen)
                        tf = time.time()
                        self.print_error("{}: flushed {} items in {:.02f} msec".format(c.name, num,(tf-t0)*1e3))
        finally:
            self.print_error("thread exit")

    @classmethod
    def _try_to_expire_old_items(cls, d_orig, num):
        d = d_orig.copy()  # yes, this is slow but this makes it so we don't need locks.
        assert len(d) > num and num > 0

        # bin the cache.dict items by 'tick' (when they were last accessed)
        bins = defaultdict(list)
        for k,v in d.items():
            tick = v[0]
            bins[tick].append((k,v))
        del d

        # now, expire the old items starting with the oldest until we
        # expire num items. note that during this loop it's possible
        # for items to get their timestamp updateed by ExpiringCache.get().
        # This loop will not detect that situation and will expire them anyway.
        # This is fine, because it's a corner case and in the interests of
        # keeping this code as simple as possible, we don't bother to guard
        # against that.
        ct = 0
        sorted_bin_keys = sorted(bins.keys())
        while ct < num and bins:
            tick = sorted_bin_keys[0]
            for tup in bins[tick]:
                del d_orig[tup[0]]  # KeyError here should never happen. if it does we want the exception because it means a bug in this code
                ct += 1
                if ct >= num:
                    break
            else:
                del bins[tick]
                del sorted_bin_keys[0]
        return ct

def get_object_size(obj_0):
    ''' Debug tool -- returns the amount of memory taken by an object in bytes
    by deeply examining its contents recursively (more accurate than
    sys.getsizeof as a result). '''
    import sys
    from numbers import Number
    from collections import Set, Mapping, deque

    try: # Python 2
        zero_depth_bases = (basestring, Number, xrange, bytearray)
        iteritems = 'iteritems'
    except NameError: # Python 3
        zero_depth_bases = (str, bytes, Number, range, bytearray)
        iteritems = 'items'

    def getsize(obj_0):
        """Recursively iterate to sum size of object & members."""
        _seen_ids = set()
        def inner(obj):
            obj_id = id(obj)
            if obj_id in _seen_ids:
                return 0
            _seen_ids.add(obj_id)
            size = sys.getsizeof(obj)
            if isinstance(obj, zero_depth_bases):
                pass # bypass remaining control flow and return
            elif isinstance(obj, (tuple, list, Set, deque)):
                size += sum(inner(i) for i in obj)
            elif isinstance(obj, Mapping) or hasattr(obj, iteritems):
                size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)())
            # Check for custom object instances - may subclass above too
            if hasattr(obj, '__dict__'):
                size += inner(vars(obj))
            if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
                size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
            return size
        return inner(obj_0)
    return getsize(obj_0)
<commit_msg>Follow-up to last commits: small performance tweak<commit_after>#!/usr/bin/env python3

import time
import threading
import queue
import weakref
from collections import defaultdict
from electroncash.util import PrintError

class ExpiringCache:
    ''' A fast cache useful for storing tens of thousands of lightweight items.

    Use this class to cache the results of functions or other computations
    when:

        1. Many identical items are repetitively created (or many duplicate
           computations are repetitively performed) during normal app
           execution, and it makes sense to cache them.
        2. The creation of said items is more computationally expensive than
           accessing this cache.
        3. The memory tradeoff is acceptable. (As with all caches, you are
           trading CPU cost for memory cost).

    And example of this is UI code or string formatting code that refreshes the
    display with (mostly) the same output over and over again. In that case it
    may make more sense to just cache the output items (such as the formatted
    amount results from format_satoshis), rather than regenerate them, as a
    performance tweak.

    ExpiringCache automatically has old items expire if `maxlen' is exceeded.

    Items are timestamped with a 'tick count' (granularity of 10 seconds per
    tick). Their timestamp is updated each time they are accessed via `get' (so
    that only the oldest items that are least useful are the first to expire on
    cache overflow).

    get() and put() are fast. A background thread is used to safely
    expire items when the cache overflows (so that get and put never stall
    to manage the cache's size and/or to flush old items).  This background
    thread runs every 10 seconds -- so caches may temporarily overflow past
    their maxlen for up to 10 seconds. '''
    def __init__(self, *, maxlen=10000, name="An Unnamed Cache"):
        assert maxlen > 0
        self.maxlen = maxlen
        self.name = name
        self.d = dict()
        _ExpiringCacheMgr.add_cache(self)
    def get(self, key, default=None):
        res = self.d.get(key)
        if res is not None:
            # cache hit
            res[0] = _ExpiringCacheMgr.tick  # update tick access time for this cache hit
            return res[1]
        # cache miss
        return default
    def put(self, key, value):
        self.d[key] = [_ExpiringCacheMgr.tick, value]
    def size_bytes(self):
        ''' Returns the cache's memory usage in bytes. This is done by doing a
        deep, recursive examination of the cache contents. '''
        return get_object_size(
            self.d.copy()  # prevent iterating over a mutating dict.
        )
    def __len__(self):
        return len(self.d)

class _ExpiringCacheMgr(PrintError):
    '''Do not use this class directly. Instead just create ExpiringCache
    instances and that will handle the creation of this object automatically
    and its lifecycle.

    This is a singleton that manages the ExpiringCaches. It creates a thread
    that wakes up every tick_interval seconds and expires old items from
    overflowing extant caches.

    Note that after the last cache is gc'd the manager thread will exit and
    this singleton object also will expire and clean itself up automatically.'''
    _lock = threading.Lock()  # used to lock _instance and self.caches
    _instance = None
    tick = 0
    tick_interval = 10.0  # seconds; we wake up this often to update 'tick' and also to expire old items for overflowing caches

    def __init__(self, add_iter=None):
        cls = type(self)
        assert not cls._instance, "_ExpiringCacheMgr is a singleton"
        super().__init__()
        cls._instance = self
        self.q = queue.Queue()
        self.caches = weakref.WeakSet()
        if add_iter:
            self.caches.update(add_iter)
        self.livect = len(self.caches)  # this is updated by add_cache and on_cache_gc below.
        self.thread = threading.Thread(target=self.mgr_thread, daemon=True)
        self.thread.start()

    @classmethod
    def add_cache(cls, *caches):
        assert caches
        new_caches = caches
        with cls._lock:
            slf = cls._instance
            if not slf:
                slf = cls(caches)
                assert slf == cls._instance
            else:
                new_caches = [c for c in caches if c not in slf.caches]
                slf.caches.update(new_caches)
            for cache in new_caches:
                # add finalizer for each new cache
                weakref.finalize(cache, cls.on_cache_gc, cache.name)
            slf.livect = len(slf.caches)

    @classmethod
    def on_cache_gc(cls, name):
        assert cls._instance
        thread2join = None
        with cls._lock:
            slf = cls._instance
            assert slf.thread.is_alive()
            slf.livect -= 1 # we need to keep this counter because the weak set doesn't have the correct length at this point yet.
            slf.print_error("Cache '{}' has been gc'd, {} still alive".format(name, slf.livect))
            if not slf.livect:  # all caches have been gc'd, kill the thread
                slf.print_error("No more caches, stopping manager thread and removing singleton")
                slf.q.put(None)  # signal thread to stop
                thread2join = slf.thread
                cls._instance = None  # kill self.
        if thread2join:
            # we do this here as defensive programming to avoid deadlocks in case
            # thread ends up taking locks in some future implementation.
            thread2join.join()

    def mgr_thread(self):
        cls = type(self)
        self.print_error("thread started")
        try:
            while True:
                try:
                    x = self.q.get(timeout=self.tick_interval)
                    return # we got a stop signal
                except queue.Empty:
                    # normal condition, we slept with nothing to do
                    pass
                cls.tick += 1
                for c in tuple(self.caches):  # prevent cache from dying while we iterate
                    len_c = len(c.d)  # capture length here as c.d may mutate and grow while this code executes.
                    if len_c > c.maxlen:
                        t0 = time.time()
                        num = cls._try_to_expire_old_items(c.d, len_c - c.maxlen)
                        tf = time.time()
                        self.print_error("{}: flushed {} items in {:.02f} msec".format(c.name, num,(tf-t0)*1e3))
        finally:
            self.print_error("thread exit")

    @classmethod
    def _try_to_expire_old_items(cls, d_orig, num):
        d = d_orig.copy()  # yes, this is slow but this makes it so we don't need locks.
        assert len(d) > num and num > 0

        # bin the cache.dict items by 'tick' (when they were last accessed)
        bins = defaultdict(list)
        for k,v in d.items():
            tick = v[0]
            bins[tick].append(k)
        del d

        # now, expire the old items starting with the oldest until we
        # expire num items. note that during this loop it's possible
        # for items to get their timestamp updateed by ExpiringCache.get().
        # This loop will not detect that situation and will expire them anyway.
        # This is fine, because it's a corner case and in the interests of
        # keeping this code as simple as possible, we don't bother to guard
        # against that.
        ct = 0
        sorted_bin_keys = sorted(bins.keys())
        while ct < num and bins:
            tick = sorted_bin_keys[0]
            for key in bins[tick]:
                del d_orig[key]  # KeyError here should never happen. if it does we want the exception because it means a bug in this code
                ct += 1
                if ct >= num:
                    break
            else:
                del bins[tick]
                del sorted_bin_keys[0]
        return ct

def get_object_size(obj_0):
    ''' Debug tool -- returns the amount of memory taken by an object in bytes
    by deeply examining its contents recursively (more accurate than
    sys.getsizeof as a result). '''
    import sys
    from numbers import Number
    from collections import Set, Mapping, deque

    try: # Python 2
        zero_depth_bases = (basestring, Number, xrange, bytearray)
        iteritems = 'iteritems'
    except NameError: # Python 3
        zero_depth_bases = (str, bytes, Number, range, bytearray)
        iteritems = 'items'

    def getsize(obj_0):
        """Recursively iterate to sum size of object & members."""
        _seen_ids = set()
        def inner(obj):
            obj_id = id(obj)
            if obj_id in _seen_ids:
                return 0
            _seen_ids.add(obj_id)
            size = sys.getsizeof(obj)
            if isinstance(obj, zero_depth_bases):
                pass # bypass remaining control flow and return
            elif isinstance(obj, (tuple, list, Set, deque)):
                size += sum(inner(i) for i in obj)
            elif isinstance(obj, Mapping) or hasattr(obj, iteritems):
                size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)())
            # Check for custom object instances - may subclass above too
            if hasattr(obj, '__dict__'):
                size += inner(vars(obj))
            if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
                size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
            return size
        return inner(obj_0)
    return getsize(obj_0)
<|endoftext|>


<commit_before>from xml.etree import ElementTree as etree

size = 100
number_offset = (11, 33)
text_offset = (30, 70)

def translate(coords, offset):
    return map(sum,zip(coords,offset))

def make_cell(row, col, color, text=None, number=None):
    cell = etree.Element("g")
    border = etree.Element("polyline")
    fill = etree.Element("polygon")
    cell.append(border)
    cell.append(fill)
    points = vertices(row, col)
    class_str = "stroke " + color
    border.set("points", points)
    fill.set("points", points)
    border.set("class", class_str)
    fill.set("class", class_str)
    if text is not None:
        textelt = etree.Element("text")
        coords = translate((row * size, col * size), text_offset)
        textelt.set("x", str(coords[0]))
        textelt.set("y", str(coords[1]))
        textelt.set("class", "fnt-museo black")
        textelt.text = str(text)
        cell.append(textelt)
    if number is not None:
        textelt = etree.Element("text")
        coords = translate((row * size, col * size), number_offset)
        textelt.set("x", str(coords[0]))
        textelt.set("y", str(coords[1]))
        textelt.set("class", "fnt-times black")
        textelt.text = str(number)
        cell.append(textelt)
    return cell

def vertices(row, col):
    order = ((0, 0), (0, 1), (1, 1), (1, 0))
    vertices = map (lambda (x,y): ((row + x) * size, (col + y) * size), order)
    return ' '.join(["%s,%s" % vertex for vertex in vertices])

def is_number(lines, i, j):
    if lines[i][j] == '*':
        return False
    if i == 0 or j == 0:
        return True
    if lines[i-1][j] == '*' and lines[i+1][j] != '*':
        return True
    if lines[i][j-1] == '*' and lines[i][j+1] != '*':
        return True
    return False
if __name__ == '__main__':
    etree.register_namespace("","http://www.w3.org/2000/svg")
    root = etree.XML('<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>')
    defs = """
    <defs>
    <style type="text/css">
    <![CDATA[
        .stroke {stroke:black;stroke-width:2.22009;stroke-linecap:square}
        .transp {fill:none}
        .black {fill:black}
        .white {fill:white}
        .fnt-times {font-weight:normal;font-size:36;font-family:'Times New Roman'}
        .fnt-museo {font-weight:normal;font-size:64;font-family:'Museo'}
    ]]>
    </style>
    </defs>"""
    root.append(etree.XML(defs))
    root.append(etree.Element("path"))
    with open('input.txt') as f:
        number = 0
        lines = [line.strip("\n") for line in f.readlines()]
        for i in range(len(lines)):
            line = lines[i]
            for j in range(len(line)):
                char = line[j]
                if char == '*':
                    root.append(make_cell(j, i, 'black'))
                else:
                    if is_number(lines, i, j):
                        number += 1
                        current_number = number
                    else:
                        current_number = None
                    root.append(make_cell(j, i, 'white', text=char, number=current_number))
            i += 1
    print etree.tostring(root, encoding='iso-8859-1')

<commit_msg>Refactor a little<commit_after>from xml.etree import ElementTree as etree
import argparse

# Cell size
size = 100
# Amount to offset the number by
number_offset = (11, 33)
# Amount to offset the text in the box by
text_offset = (30, 70)

# Giant style string with the CSS for the SVG. problem? no problem.
stylestring = """
    <defs>
    <style type="text/css">
    <![CDATA[
        .stroke {stroke:black;stroke-width:2.22009;stroke-linecap:square}
        .transp {fill:none}
        .black {fill:black}
        .white {fill:white}
        .fnt-times {font-weight:normal;font-size:36;font-family:'Times New Roman'}
        .fnt-museo {font-weight:normal;font-size:64;font-family:'Museo'}
    ]]>
    </style>
    </defs>"""
def translate(coords, offset):
    return map(sum,zip(coords,offset))

def make_cell(col, row, color, text=None, number=None):
    cell = etree.Element("g")
    border = etree.Element("polyline")
    fill = etree.Element("polygon")
    cell.append(border)
    cell.append(fill)
    points = vertices(row, col)
    class_str = "stroke " + color
    border.set("points", points)
    fill.set("points", points)
    border.set("class", class_str)
    fill.set("class", class_str)
    if text is not None:
        textelt = etree.Element("text")
        coords = translate((row * size, col * size), text_offset)
        textelt.set("x", str(coords[0]))
        textelt.set("y", str(coords[1]))
        textelt.set("class", "fnt-museo black")
        textelt.text = str(text)
        cell.append(textelt)
    if number is not None:
        textelt = etree.Element("text")
        coords = translate((row * size, col * size), number_offset)
        textelt.set("x", str(coords[0]))
        textelt.set("y", str(coords[1]))
        textelt.set("class", "fnt-times black")
        textelt.text = str(number)
        cell.append(textelt)
    return cell

def vertices(row, col):
    order = ((0, 0), (0, 1), (1, 1), (1, 0))
    vertices = map (lambda (x,y): ((row + x) * size, (col + y) * size), order)
    return ' '.join(["%s,%s" % vertex for vertex in vertices])

def is_start_of_word(lines, i, j):
    """
    Checks if there is a word starting at (i,j)
    """
    if lines[i][j] == '*':
        return False
    if i == 0 or j == 0:
        return True
    if lines[i-1][j] == '*' and lines[i+1][j] != '*':
        return True
    if lines[i][j-1] == '*' and lines[i][j+1] != '*':
        return True
    return False

def create_svg(lines):
    """Takes a list of lines from a file with newlines stripped and creates a
    SVG
    """
    root = etree.XML('<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>')
    root.append(etree.XML(stylestring))
    # number is the current word number, for numbering the words in the
    # crossword
    number = 0
    for i in range(len(lines)):
        line = lines[i]
        for j in range(len(line)):
            char = line[j]
            if char == '*':
                # Add a blank black cell
                root.append(make_cell(i, j, 'black'))
            else:
                if is_start_of_word(lines, i, j):
                    number += 1
                    current_number = number
                else:
                    current_number = None
                # Add a white cell, possibly with text
                root.append(make_cell(i, j, 'white', text=char, number=current_number))
    return root

if __name__ == '__main__':
    parser = argparse.ArgumentParser(prog='make_crossword')
    parser.add_argument('crossword_file', type=str)
    args = parser.parse_args()
    etree.register_namespace("","http://www.w3.org/2000/svg")
    with open(args.crossword_file, 'r') as f:
        lines = [line.strip("\n") for line in f.readlines()]
    svg = create_svg(lines)
    print etree.tostring(svg, encoding='iso-8859-1')

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# pylint: disable=bad-continuation
""" ‘Double Click’ – Extensions to `Click <http://click.pocoo.org/4/>`_.
"""
# Copyright ©  2015 Jürgen Hermann <jh@web.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, unicode_literals, print_function

import os
import re
import sys

import configobj
from bunch import Bunch
from click import *  # pylint: disable=wildcard-import
from click import __all__

from .._compat import encode_filename

__all__ = list(__all__) + [
    'pretty_path', 'serror',
    'LoggedFailure', 'AliasedGroup', 'Configuration',
]
__all__ = [encode_filename(_) for _ in __all__]

def pretty_path(path, _home_re=re.compile('^' + re.escape(os.path.expanduser('~') + os.sep))):
    """Prettify path for humans, and make it Unicode."""
    path = format_filename(path)
    path = _home_re.sub('~' + os.sep, path)
    return path

def serror(message, *args, **kwargs):
    """Print a styled error message, while using any arguments to format the message."""
    if args or kwargs:
        message = message.format(*args, **kwargs)
    return secho(message, fg='white', bg='red', bold=True)

class LoggedFailure(UsageError):
    """Report a failure condition to the user."""

    def __init__(self, message):
        message = style(message, fg='white', bg='red', bold=True)
        UsageError.__init__(self, message)

class AliasedGroup(Group):
    """ A command group with alias names.

        Inherit from this class and define a ``MAP`` class variable,
        which is a mapping from alias names to canonical command names.
        Then use that derived class as the ``cls`` parameter for a
        ``click.group`` decorator.
    """

    MAP = {}

    def get_command(self, ctx, cmd_name):
        """Map some aliases to their 'real' names."""
        cmd_name = self.MAP.get(cmd_name, cmd_name)
        return super(AliasedGroup, self).get_command(ctx, cmd_name)

class Configuration(object):
    """ Configuration container that is initialized early in the main command.

        The default instance is available via the Click context as ``ctx.obj.cfg``.
        Configuration is lazily loaded, on first access.
    """

    NO_DEFAULT = object()

    @classmethod
    def from_context(cls, ctx, config_paths=None):
        """Create a configuration object, and initialize the Click context with it."""
        if ctx.obj is None:
            ctx.obj = Bunch()
        ctx.obj.cfg = cls(ctx.info_name, config_paths)
        return ctx.obj.cfg

    def __init__(self, name, config_paths=None):
        """ Set up static knowledge about configuration.

            ``config_paths`` is a list of PATHs to config files or directories.
            If that is non-empty, exactly those paths will be considered; an empty
            path element represents the default locations (just like in MANPATH).

            If the environment variable ``‹prefix›_CONFIG`` is set, its value will
            be appended to the default locations.
        """
        self.values = configobj.ConfigObj({}, encoding='utf-8', default_encoding='utf-8')
        self.name = name
        self.config_paths = []
        self.loaded = False

        env_config = os.environ.get((self.name + '-config').upper().replace('-', '_'), '')
        defaults = [
            '/etc/{}.conf'.format(self.name),
            os.path.join(get_app_dir(self.name) + '.conf'),
        ] + [i for i in env_config.split(os.pathsep) if i]

        for path in config_paths or []:
            for name in path.split(os.pathsep):
                if name:
                    self.config_paths.append(name)
                else:
                    self.config_paths.extend(defaults)

        if not self.config_paths:
            self.config_paths = defaults

    def locations(self, exists=True):
        """ Return the location of the config file(s).

            A given directory will be scanned for ``*.conf`` files, in alphabetical order.
            Any duplicates will be eliminated.

            If ``exists`` is True, only existing configuration locations are returned.
        """
        result = []
        for config_files in self.config_paths:
            if not config_files:
                continue
            if os.path.isdir(config_files):
                config_files = [os.path.join(config_files, i)
                                for i in os.listdir(config_files)
                                if i.endswith('.conf')]
            else:
                config_files = [config_files]
            for config_file in config_files:
                if not exists or os.path.exists(config_file):
                    config_file = os.path.abspath(config_file)
                    if config_file in result:
                        result.remove(config_file)
                    result.append(config_file)
        return result

    def load(self):
        """Load configuration from the defined locations."""
        if not self.loaded:
            self.values = configobj.ConfigObj({}, encoding='utf-8', default_encoding='utf-8')
            for path in self.locations():
                part = configobj.ConfigObj(infile=path, encoding='utf-8', default_encoding='utf-8')
                self.values.merge(part)
            self.loaded = True
        return self.values

    def dump(self, to=None):  # pylint: disable=invalid-name
        """Dump the merged configuration to a stream or stdout."""
        self.load().write(to or sys.stdout)

    def section(self, ctx):
        """Return section of the config for a specific context (sub-command)."""
        values = self.load()
        try:
            return values[ctx.info_name]
        except KeyError:
            raise LoggedFailure("Configuration section '{}' not found!".format(ctx.info_name))

    def get(self, name, default=NO_DEFAULT):
        """Return the specified key from the root section."""
        values = self.load()
        try:
            return values[name]
        except KeyError:
            if default is self.NO_DEFAULT:
                raise LoggedFailure("Configuration value '{}' not found in root section!".format(name))
            return default
<commit_msg>:bug: need to sort any config.d listing<commit_after># -*- coding: utf-8 -*-
# pylint: disable=bad-continuation
""" ‘Double Click’ – Extensions to `Click <http://click.pocoo.org/4/>`_.
"""
# Copyright ©  2015 Jürgen Hermann <jh@web.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, unicode_literals, print_function

import os
import re
import sys

import configobj
from bunch import Bunch
from click import *  # pylint: disable=wildcard-import
from click import __all__

from .._compat import encode_filename

__all__ = list(__all__) + [
    'pretty_path', 'serror',
    'LoggedFailure', 'AliasedGroup', 'Configuration',
]
__all__ = [encode_filename(_) for _ in __all__]

def pretty_path(path, _home_re=re.compile('^' + re.escape(os.path.expanduser('~') + os.sep))):
    """Prettify path for humans, and make it Unicode."""
    path = format_filename(path)
    path = _home_re.sub('~' + os.sep, path)
    return path

def serror(message, *args, **kwargs):
    """Print a styled error message, while using any arguments to format the message."""
    if args or kwargs:
        message = message.format(*args, **kwargs)
    return secho(message, fg='white', bg='red', bold=True)

class LoggedFailure(UsageError):
    """Report a failure condition to the user."""

    def __init__(self, message):
        message = style(message, fg='white', bg='red', bold=True)
        UsageError.__init__(self, message)

class AliasedGroup(Group):
    """ A command group with alias names.

        Inherit from this class and define a ``MAP`` class variable,
        which is a mapping from alias names to canonical command names.
        Then use that derived class as the ``cls`` parameter for a
        ``click.group`` decorator.
    """

    MAP = {}

    def get_command(self, ctx, cmd_name):
        """Map some aliases to their 'real' names."""
        cmd_name = self.MAP.get(cmd_name, cmd_name)
        return super(AliasedGroup, self).get_command(ctx, cmd_name)

class Configuration(object):
    """ Configuration container that is initialized early in the main command.

        The default instance is available via the Click context as ``ctx.obj.cfg``.
        Configuration is lazily loaded, on first access.
    """

    NO_DEFAULT = object()

    @classmethod
    def from_context(cls, ctx, config_paths=None):
        """Create a configuration object, and initialize the Click context with it."""
        if ctx.obj is None:
            ctx.obj = Bunch()
        ctx.obj.cfg = cls(ctx.info_name, config_paths)
        return ctx.obj.cfg

    def __init__(self, name, config_paths=None):
        """ Set up static knowledge about configuration.

            ``config_paths`` is a list of PATHs to config files or directories.
            If that is non-empty, exactly those paths will be considered; an empty
            path element represents the default locations (just like in MANPATH).

            If the environment variable ``‹prefix›_CONFIG`` is set, its value will
            be appended to the default locations.
        """
        self.values = configobj.ConfigObj({}, encoding='utf-8', default_encoding='utf-8')
        self.name = name
        self.config_paths = []
        self.loaded = False

        env_config = os.environ.get((self.name + '-config').upper().replace('-', '_'), '')
        defaults = [
            '/etc/{}.conf'.format(self.name),
            os.path.join(get_app_dir(self.name) + '.conf'),
        ] + [i for i in env_config.split(os.pathsep) if i]

        for path in config_paths or []:
            for name in path.split(os.pathsep):
                if name:
                    self.config_paths.append(name)
                else:
                    self.config_paths.extend(defaults)

        if not self.config_paths:
            self.config_paths = defaults

    def locations(self, exists=True):
        """ Return the location of the config file(s).

            A given directory will be scanned for ``*.conf`` files, in alphabetical order.
            Any duplicates will be eliminated.

            If ``exists`` is True, only existing configuration locations are returned.
        """
        result = []
        for config_files in self.config_paths:
            if not config_files:
                continue
            if os.path.isdir(config_files):
                config_files = [os.path.join(config_files, i)
                                for i in sorted(os.listdir(config_files))
                                if i.endswith('.conf')]
            else:
                config_files = [config_files]
            for config_file in config_files:
                if not exists or os.path.exists(config_file):
                    config_file = os.path.abspath(config_file)
                    if config_file in result:
                        result.remove(config_file)
                    result.append(config_file)
        return result

    def load(self):
        """Load configuration from the defined locations."""
        if not self.loaded:
            self.values = configobj.ConfigObj({}, encoding='utf-8', default_encoding='utf-8')
            for path in self.locations():
                part = configobj.ConfigObj(infile=path, encoding='utf-8', default_encoding='utf-8')
                self.values.merge(part)
            self.loaded = True
        return self.values

    def dump(self, to=None):  # pylint: disable=invalid-name
        """Dump the merged configuration to a stream or stdout."""
        self.load().write(to or sys.stdout)

    def section(self, ctx):
        """Return section of the config for a specific context (sub-command)."""
        values = self.load()
        try:
            return values[ctx.info_name]
        except KeyError:
            raise LoggedFailure("Configuration section '{}' not found!".format(ctx.info_name))

    def get(self, name, default=NO_DEFAULT):
        """Return the specified key from the root section."""
        values = self.load()
        try:
            return values[name]
        except KeyError:
            if default is self.NO_DEFAULT:
                raise LoggedFailure("Configuration value '{}' not found in root section!".format(name))
            return default
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# PyKrige documentation build configuration file, created by
# sphinx-quickstart on Wed Mar  1 18:34:53 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import shlex
import sphinx_rtd_theme
import pykrige

from recommonmark.parser import CommonMarkParser

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../'))
sys.path.insert(0, os.path.abspath('sphinxext'))

from github_link import make_linkcode_resolve

source_parsers = {
    '.md': CommonMarkParser,
}
# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.mathjax',
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinxcontrib.napoleon',
    'sphinx_gallery.gen_gallery',
    'sphinx.ext.linkcode'
]

autosummary_generate = True

autodoc_default_flags = ['members', 'inherited-members']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
source_suffix = ['.rst', '.md']

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

sphinx_gallery_conf = {
    # path to your examples scripts
    'examples_dirs' : '../examples',
    # path where to save gallery generated examples
    'gallery_dirs'  : 'examples',
    'filename_pattern'  : '/.*.py'
}

# General information about the project.
project = 'PyKrige'
copyright = '2017, PyKrige developers'
author = 'PyKrige developers'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = pykrige.__version__
# The full version, including alpha/beta/rc tags.
release = pykrige.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'PyKrigedoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',

# Latex figure (float) alignment
#'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  (master_doc, 'PyKrige.tex', 'PyKrige Documentation',
   'PyKrige developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'pykrige', 'PyKrige Documentation',
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  (master_doc, 'PyKrige', 'PyKrige Documentation',
   author, 'PyKrige', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# The following is used by sphinx.ext.linkcode to provide links to github
linkcode_resolve = make_linkcode_resolve('pykrige',
                                         u'https://github.com/bsmurphy/'
                                         'PyKrige/blob/{revision}/'
                                         '{package}/{path}#L{lineno}')
<commit_msg>Setting Agg backend for matplotlib<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# PyKrige documentation build configuration file, created by
# sphinx-quickstart on Wed Mar  1 18:34:53 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import shlex

import matplotlib
matplotlib.use("Agg")

import sphinx_rtd_theme
import pykrige

from recommonmark.parser import CommonMarkParser

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../'))
sys.path.insert(0, os.path.abspath('sphinxext'))

from github_link import make_linkcode_resolve

source_parsers = {
    '.md': CommonMarkParser,
}
# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.mathjax',
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinxcontrib.napoleon',
    'sphinx_gallery.gen_gallery',
    'sphinx.ext.linkcode'
]

autosummary_generate = True

autodoc_default_flags = ['members', 'inherited-members']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
source_suffix = ['.rst', '.md']

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

sphinx_gallery_conf = {
    # path to your examples scripts
    'examples_dirs' : '../examples',
    # path where to save gallery generated examples
    'gallery_dirs'  : 'examples',
    'filename_pattern'  : '/.*.py'
}

# General information about the project.
project = 'PyKrige'
copyright = '2017, PyKrige developers'
author = 'PyKrige developers'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = pykrige.__version__
# The full version, including alpha/beta/rc tags.
release = pykrige.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'PyKrigedoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',

# Latex figure (float) alignment
#'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  (master_doc, 'PyKrige.tex', 'PyKrige Documentation',
   'PyKrige developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'pykrige', 'PyKrige Documentation',
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  (master_doc, 'PyKrige', 'PyKrige Documentation',
   author, 'PyKrige', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# The following is used by sphinx.ext.linkcode to provide links to github
linkcode_resolve = make_linkcode_resolve('pykrige',
                                         u'https://github.com/bsmurphy/'
                                         'PyKrige/blob/{revision}/'
                                         '{package}/{path}#L{lineno}')
<|endoftext|>


<commit_before>#!/usr/bin/env python
# coding=utf-8

"""
ola channel mapper.

    read a configuration file and map channels from one universe to a second.

    history:
        see git commits

    todo:
        ~ all fine :-)
"""

import sys
import time
import os
import array

from configdict import ConfigDict
from olathreaded import OLAThread, OLAThread_States

version = """08.03.2016 12:30 stefan"""

##########################################
# globals

##########################################
# functions

##########################################
# classes

class OLAMapper(OLAThread):
    """Class that extends on OLAThread and implements the Mapper functions."""

    def __init__(self, config):
        """init mapper things."""
        # super(OLAThread, self).__init__()
        OLAThread.__init__(self)

        self.config = config
        # print("config: {}".format(self.config))

        self.universe = self.config['universe']['output']
        # self.channel_count = 512
        # self.channel_count = 50
        self.channel_count = self.config['universe']['channel_count']
        self.channels_out = array.array('B')

        # self.channels = []
        for channel_index in range(0, self.channel_count):
            self.channels_out.append(0)

        # timing things
        self.duration = 0
        self.calls = 0

    def print_measurements(self):
        """print duration statistics on exit."""
        # print duration meassurements:
        print(
            (
                "map_channels:\n" +
                "  sum duration:  {:>10}s\n" +
                "  sum calls:     {:>10}\n" +
                "  duration/call: {:>10}ms/call\n"
            ).format(
                self.duration,
                self.calls,
                ((self.duration / self.calls) / 1000)
            )
        )

    def ola_connected(self):
        """register receive callback and switch to running mode."""
        self.client.RegisterUniverse(
            self.config['universe']['input'],
            self.client.REGISTER,
            self.dmx_receive_frame
        )
        # python3 syntax
        # super().ola_connected()
        # python2 syntax
        # super(OLAThread, self).ola_connected()
        # explicit call
        OLAThread.ola_connected(self)

    def dmx_receive_frame(self, data):
        """receive one dmx frame."""
        # print(data)
        # meassure duration:
        start = time.time()
        self.map_channels(data)
        stop = time.time()
        duration = stop - start
        self.duration += duration
        self.calls += 1
        # temp_array = array.array('B')
        # for channel_index in range(0, self.channel_count):
        #     temp_array.append(self.channels[channel_index])

    def map_channels(self, data_input):
        """remap channels according to map tabel."""
        # print("map channels:")
        # print("data_input: {}".format(data_input))
        data_input_length = data_input.buffer_info()[1]
        # print("data_input_length: {}".format(data_input_length))
        # print("map: {}".format(self.config['map']))

        map = self.config['map']

        data_output = array.array('B')

        # for channel_index in range(0, data_input_length):
        #     data_output.append(data_input[channel_index])
        channel_output_count_temp = len(map['channels'])
        if map['repeat'] is True:
            channel_output_count_temp = self.channel_count
        elif isinstance(map['repeat'], int):
            channel_output_count_temp = len(map['channels']) * map['repeat']

        for channel_output_index in range(0, channel_output_count_temp):
            # calculate map_index
            map_index = channel_output_index % len(map['channels'])
            # print("map_index: {}".format(map_index))

            # get map channel
            map_value = map['channels'][map_index]
            if map['repeat'] and map['offset']:
                loop_index = channel_output_index // len(map['channels'])
                if isinstance(map['repeat'], int) and map['repeat_reverse']:
                    map_value = map_value + (
                        ((map['repeat']-1) - loop_index) *
                        map['offset_count']
                    )
                else:
                    map_value = map_value + (loop_index * map['offset_count'])
            # print("map_value: {}".format(map_value))

            # check if map_value is in range of input channels
            if (
                (map_value < data_input_length) and
                (map_value < data_input_length)
            ):
                try:
                    self.channels_out[channel_output_index] = (
                        data_input[map_value]
                    )
                except Exception as e:
                    print(
                        (
                            "additional info:\n" +
                            "  channel_output_index: {}\n" +
                            "  len(self.channels_out): {}\n" +
                            "  map_value: {}\n"
                        ).format(
                            channel_output_index,
                            len(self.channels_out),
                            map_value
                        )
                    )
                    raise
            else:
                # don't alter data
                pass

        self.dmx_send_frame(
            self.config['universe']['output'],
            self.channels_out
        )

##########################################
if __name__ == '__main__':

    print(42*'*')
    print('Python Version: ' + sys.version)
    print(42*'*')
    print(__doc__)
    print(42*'*')

    # parse arguments
    filename = "map.json"
    # only use args after script name
    arg = sys.argv[1:]
    if not arg:
        print("using standard values.")
        print(" Allowed parameters:")
        print("   filename for config file       (default='map.json')")
        print("")
    else:
        filename = arg[0]
        # if len(arg) > 1:
        #     pixel_count = int(arg[1])
    # print parsed argument values
    print('''values:
        filename :{}
    '''.format(filename))

    default_config = {
        'universe': {
            'input': 1,
            'output': 2,
            'channel_count': 300,
        },
        'map': {
            'channels': [
                -1,
                -1,
                -1,
                -1,
                30,
                31,
                -1,
                -1,
                -1,
                -1,
                22,
                23,
                -1,
                -1,
                -1,
                -1,
                14,
                15,
                -1,
                -1,
                -1,
                -1,
                6,
                7,
                28,
                29,
                26,
                27,
                24,
                25,
                20,
                21,
                18,
                19,
                16,
                17,
                12,
                13,
                10,
                11,
                8,
                9,
                4,
                5,
                2,
                3,
                0,
                1,
            ],
            'repeat': 5,
            'repeat_reverse': True,
            'offset': True,
            'offset_count': 32,
        },
    }
    my_config = ConfigDict(default_config, filename)
    print("my_config.config: {}".format(my_config.config))

    my_mapper = OLAMapper(my_config.config)

    my_mapper.start_ola()

    # wait for user to hit key.
    try:
        raw_input(
            "\n\n" +
            42*'*' +
            "\nhit a key to stop the mapper\n" +
            42*'*' +
            "\n\n"
        )
    except KeyboardInterrupt:
        print("\nstop.")
    except:
        print("\nstop.")

    # blocks untill thread has joined.
    my_mapper.stop_ola()

    my_mapper.print_measurements()

    # as last thing we save the current configuration.
    print("\nwrite config.")
    my_config.write_to_file()

    # ###########################################
<commit_msg>fixed formating<commit_after>#!/usr/bin/env python
# coding=utf-8

"""
ola channel mapper.

    read a configuration file and map channels from one universe to a second.

    history:
        see git commits

    todo:
        ~ all fine :-)
"""

import sys
import time
import os
import array

from configdict import ConfigDict
from olathreaded import OLAThread, OLAThread_States

version = """08.03.2016 12:30 stefan"""

##########################################
# globals

##########################################
# functions

##########################################
# classes

class OLAMapper(OLAThread):
    """Class that extends on OLAThread and implements the Mapper functions."""

    def __init__(self, config):
        """init mapper things."""
        # super(OLAThread, self).__init__()
        OLAThread.__init__(self)

        self.config = config
        # print("config: {}".format(self.config))

        self.universe = self.config['universe']['output']
        # self.channel_count = 512
        # self.channel_count = 50
        self.channel_count = self.config['universe']['channel_count']
        self.channels_out = array.array('B')

        # self.channels = []
        for channel_index in range(0, self.channel_count):
            self.channels_out.append(0)

        # timing things
        self.duration = 0
        self.calls = 0

    def print_measurements(self):
        """print duration statistics on exit."""
        # print duration meassurements:
        print(
            (
                "map_channels:\n" +
                "  sum duration:  {:>10f}s\n" +
                "  sum calls:     {:>10}\n" +
                "  duration/call: {:>10f}ms/call\n"
            ).format(
                self.duration,
                self.calls,
                ((self.duration / self.calls) / 1000)
            )
        )

    def ola_connected(self):
        """register receive callback and switch to running mode."""
        self.client.RegisterUniverse(
            self.config['universe']['input'],
            self.client.REGISTER,
            self.dmx_receive_frame
        )
        # python3 syntax
        # super().ola_connected()
        # python2 syntax
        # super(OLAThread, self).ola_connected()
        # explicit call
        OLAThread.ola_connected(self)

    def dmx_receive_frame(self, data):
        """receive one dmx frame."""
        # print(data)
        # meassure duration:
        start = time.time()
        self.map_channels(data)
        stop = time.time()
        duration = stop - start
        self.duration += duration
        self.calls += 1
        # temp_array = array.array('B')
        # for channel_index in range(0, self.channel_count):
        #     temp_array.append(self.channels[channel_index])

    def map_channels(self, data_input):
        """remap channels according to map tabel."""
        # print("map channels:")
        # print("data_input: {}".format(data_input))
        data_input_length = data_input.buffer_info()[1]
        # print("data_input_length: {}".format(data_input_length))
        # print("map: {}".format(self.config['map']))

        map = self.config['map']

        data_output = array.array('B')

        # for channel_index in range(0, data_input_length):
        #     data_output.append(data_input[channel_index])
        channel_output_count_temp = len(map['channels'])
        if map['repeat'] is True:
            channel_output_count_temp = self.channel_count
        elif isinstance(map['repeat'], int):
            channel_output_count_temp = len(map['channels']) * map['repeat']

        for channel_output_index in range(0, channel_output_count_temp):
            # calculate map_index
            map_index = channel_output_index % len(map['channels'])
            # print("map_index: {}".format(map_index))

            # get map channel
            map_value = map['channels'][map_index]
            if map['repeat'] and map['offset']:
                loop_index = channel_output_index // len(map['channels'])
                if isinstance(map['repeat'], int) and map['repeat_reverse']:
                    map_value = map_value + (
                        ((map['repeat']-1) - loop_index) *
                        map['offset_count']
                    )
                else:
                    map_value = map_value + (loop_index * map['offset_count'])
            # print("map_value: {}".format(map_value))

            # check if map_value is in range of input channels
            if (
                (map_value < data_input_length) and
                (map_value < data_input_length)
            ):
                try:
                    self.channels_out[channel_output_index] = (
                        data_input[map_value]
                    )
                except Exception as e:
                    print(
                        (
                            "additional info:\n" +
                            "  channel_output_index: {}\n" +
                            "  len(self.channels_out): {}\n" +
                            "  map_value: {}\n"
                        ).format(
                            channel_output_index,
                            len(self.channels_out),
                            map_value
                        )
                    )
                    raise
            else:
                # don't alter data
                pass

        self.dmx_send_frame(
            self.config['universe']['output'],
            self.channels_out
        )

##########################################
if __name__ == '__main__':

    print(42*'*')
    print('Python Version: ' + sys.version)
    print(42*'*')
    print(__doc__)
    print(42*'*')

    # parse arguments
    filename = "map.json"
    # only use args after script name
    arg = sys.argv[1:]
    if not arg:
        print("using standard values.")
        print(" Allowed parameters:")
        print("   filename for config file       (default='map.json')")
        print("")
    else:
        filename = arg[0]
        # if len(arg) > 1:
        #     pixel_count = int(arg[1])
    # print parsed argument values
    print('''values:
        filename :{}
    '''.format(filename))

    default_config = {
        'universe': {
            'input': 1,
            'output': 2,
            'channel_count': 300,
        },
        'map': {
            'channels': [
                -1,
                -1,
                -1,
                -1,
                30,
                31,
                -1,
                -1,
                -1,
                -1,
                22,
                23,
                -1,
                -1,
                -1,
                -1,
                14,
                15,
                -1,
                -1,
                -1,
                -1,
                6,
                7,
                28,
                29,
                26,
                27,
                24,
                25,
                20,
                21,
                18,
                19,
                16,
                17,
                12,
                13,
                10,
                11,
                8,
                9,
                4,
                5,
                2,
                3,
                0,
                1,
            ],
            'repeat': 5,
            'repeat_reverse': True,
            'offset': True,
            'offset_count': 32,
        },
    }
    my_config = ConfigDict(default_config, filename)
    print("my_config.config: {}".format(my_config.config))

    my_mapper = OLAMapper(my_config.config)

    my_mapper.start_ola()

    # wait for user to hit key.
    try:
        raw_input(
            "\n\n" +
            42*'*' +
            "\nhit a key to stop the mapper\n" +
            42*'*' +
            "\n\n"
        )
    except KeyboardInterrupt:
        print("\nstop.")
    except:
        print("\nstop.")

    # blocks untill thread has joined.
    my_mapper.stop_ola()

    my_mapper.print_measurements()

    # as last thing we save the current configuration.
    print("\nwrite config.")
    my_config.write_to_file()

    # ###########################################
<|endoftext|>