"""
pricing.price
~~~~~~~~~~~

Classes representing Price.

:copyright: (c) 2018 by Joseph Black.
:license: MIT, see LICENSE for more details.
"""

from decimal import Decimal
import re

from zope.interface import implementer
from zope.component import queryUtility
import attr
from attr.validators import instance_of

import babel
from . import babel_numbers
from .interfaces import IPrice, IExchange, ICurrencyFormat
from .exceptions import (
    CurrencyMismatch, ExchangeRateNotFound, InvalidOperandType)

LC_NUMERIC = babel.default_locale('LC_NUMERIC')

__all__ = ['LC_NUMERIC', 'Price', 'XPrice']

def sub_symbols(pattern, code, symbol):
    """Substitutes symbols in CLDR number pattern."""
    return pattern.replace('¤¤', code).replace('¤', symbol)

def amount_converter(obj):
    """Converts amount value from several types into Decimal."""
    if isinstance(obj, Decimal):
        return obj
    elif isinstance(obj, (str, int, float)):
        return Decimal(str(obj))
    else:
        raise ValueError('do not know how to convert: {}'.format(type(obj)))

@implementer(IPrice)
@attr.s(frozen=True, hash=False, cmp=False, repr=False)
class Price:
    """Price class with a amount and ISO4217 currency code.

    :param amount Decimal:
        Amount of units for price. Converts from (str, int, float).
    :param currency str: A ISO4217 currency code.
    :return: a Price object.
    :rtype: :inst:`Price`

    Usage::

        >>> Price('32.09', 'USD')
        USD 32.09
        ... Price('234.242335', 'BTC').format()
        BTC 234.242335
    """

    amount: Decimal = attr.ib(
        default='0.00',
        converter=amount_converter,
        validator=instance_of(Decimal))
    currency: str = attr.ib(
        default='USD',
        validator=[
            instance_of(str)
        ])

    @currency.validator
    def validate_currency(self, attribute, value):
        if not bool(re.compile(r'^[A-Z]{3}$').match(value)):
            raise ValueError('Invalid currency: {}'.format(value))

    def __hash__(self):
        return hash((self.amount, self.currency))

    def __repr__(self):
        return "{} {}".format(self.currency, self.amount)

    def __str__(self):
        return u'{} {:,f}'.format(self.currency, self.amount)

    def __lt__(self, other):
        if not isinstance(other, Price):
            raise InvalidOperandType(other, '<')
        elif other.currency != self.currency:
            raise CurrencyMismatch(self.currency, other.currency, '<')
        else:
            return self.amount < other.amount

    def __le__(self, other):
        if not isinstance(other, Price):
            raise InvalidOperandType(other, '<=')
        elif other.currency != self.currency:
            raise CurrencyMismatch(self.currency, other.currency, '<=')
        else:
            return self.amount <= other.amount

    def __eq__(self, other):
        if isinstance(other, Price):
            return ((self.amount == other.amount) and
                    (self.currency == other.currency))
        return False

    def __ne__(self, other):
        return not self == other

    def __gt__(self, other):
        if not isinstance(other, Price):
            raise InvalidOperandType(other, '>')
        elif other.currency != self.currency:
            raise CurrencyMismatch(self.currency, other.currency, '>')
        else:
            return self.amount > other.amount

    def __ge__(self, other):
        if not isinstance(other, Price):
            raise InvalidOperandType(other, '>=')
        elif other.currency != self.currency:
            raise CurrencyMismatch(self.currency, other.currency, '>=')
        else:
            return self.amount >= other.amount

    def __nonzero__(self):
        return self.__bool__()

    def __bool__(self):
        """
        Considering Price a numeric type (on ``amount``):

        bool(Price(2, 'XXX')) --> True
        bool(Price(0, 'XXX')) --> False
        """
        return bool(self.amount)

    def __add__(self, other):
        if isinstance(other, Price):
            if other.currency != self.currency:
                raise CurrencyMismatch(self.currency, other.currency, '+')
            other = other.amount
        amount = self.amount + other
        return self.__class__(amount, self.currency)

    def __radd__(self, other):
        return self.__add__(other)

    def __sub__(self, other):
        if isinstance(other, Price):
            if other.currency != self.currency:
                raise CurrencyMismatch(self.currency, other.currency, '-')
            other = other.amount
        amount = self.amount - other
        return self.__class__(amount, self.currency)

    def __rsub__(self, other):
        return (-self).__add__(other)

    def __mul__(self, other):
        if isinstance(other, Price):
            raise TypeError("multiplication is unsupported between "
                            "two price objects")
        amount = self.amount * other
        return self.__class__(amount, self.currency)

    def __rmul__(self, other):
        return self.__mul__(other)

    def __div__(self, other):
        return self.__truediv__(other)

    def __truediv__(self, other):
        if isinstance(other, Price):
            if other.currency != self.currency:
                raise CurrencyMismatch(self.currency, other.currency, '/')
            elif other.amount == 0:
                raise ZeroDivisionError()
            return self.amount / other.amount
        else:
            if other == 0:
                raise ZeroDivisionError()
            amount = self.amount / other
            return self.__class__(amount, self.currency)

    def __floordiv__(self, other):
        if isinstance(other, Price):
            if other.currency != self.currency:
                raise CurrencyMismatch(self.currency, other.currency, '//')
            elif other.amount == 0:
                raise ZeroDivisionError()
            return self.amount // other.amount
        else:
            if other == 0:
                raise ZeroDivisionError()
            amount = self.amount // other
            return self.__class__(amount, self.currency)

    def __mod__(self, other):
        if isinstance(other, Price):
            raise TypeError("modulo is unsupported between two '{}' "
                            "objects".format(self.__class__.__name__))
        if other == 0:
            raise ZeroDivisionError()
        amount = self.amount % other
        return self.__class__(amount, self.currency)

    def __divmod__(self, other):
        if isinstance(other, Price):
            if other.currency != self.currency:
                raise CurrencyMismatch(self.currency, other.currency, 'divmod')
            elif other.amount == 0:
                raise ZeroDivisionError()
            return divmod(self.amount, other.amount)
        else:
            if other == 0:
                raise ZeroDivisionError()
            whole, remainder = divmod(self.amount, other)
            return (self.__class__(whole, self.currency),
                    self.__class__(remainder, self.currency))

    def __pow__(self, other):
        if isinstance(other, Price):
            raise TypeError("power operator is unsupported between two '{}' "
                            "objects".format(self.__class__.__name__))
        amount = self.amount ** other
        return self.__class__(amount, self.currency)

    def __neg__(self):
        return self.__class__(-self.amount, self.currency)

    def __pos__(self):
        return self.__class__(+self.amount, self.currency)

    def __abs__(self):
        return self.__class__(abs(self.amount), self.currency)

    def __int__(self):
        return int(self.amount)

    def __float__(self):
        return float(self.amount)

    def __round__(self, ndigits=0):
        return self.__class__(round(self.amount, ndigits), self.currency)

    def __composite_values__(self):
        return self.amount, self.currency

    def to(self, currency):
        """Return equivalent price object in another currency"""
        if currency == self.currency:
            return self
        exchange = queryUtility(IExchange)
        rate = exchange.quotation(self.currency, currency)
        if rate is None:
            raise ExchangeRateNotFound(
                exchange.backend_name, self.currency, currency)
        amount = self.amount * rate
        return self.__class__(amount, currency)

    def format(self, locale=LC_NUMERIC, pattern=None, format_type='standard',
               **kwargs):
        """Return a locale-aware, currency-formatted string.

        This method emulates babel.numbers.format_currency().

        A specific locale identifier (language[_territory]) can be passed,
        otherwise the system's default locale will be used. A custom
        formatting pattern of the form "¤#,##0.00;(¤#,##0.00)"
        (positive[;negative]) can also be passed, otherwise it will be
        determined from the locale and the CLDR (Unicode Common Locale Data
        Repository) included with Babel.

        >>> m = Price('1234.567', 'EUR')
        >>> m.format() # assuming the system's locale is 'en_US'
        €1,234.57
        >>> m.format('de_DE') # German formatting
        1.234,57 €
        >>> m.format('de', '#,##0 ¤') # German formatting (short), no cents
        1.235 €
        >>> m.format(pattern='#,##0.00 ¤¤¤') # Default locale, full name
        1,235.57 euro

        Learn more about this formatting syntaxis at:
        http://www.unicode.org/reports/tr35/tr35-numbers.html
        """

        currency_format = queryUtility(ICurrencyFormat, name=self.currency)
        if currency_format:
            format = sub_symbols(pattern or currency_format.format,
                                 currency_format.code, currency_format.symbol)
            # format = (pattern or currency_format.format).replace(
            #     '¤¤', currency_format.code).replace(
            #         '¤', currency_format.symbol)
            kwargs.setdefault('format', format)
            kwargs.setdefault(
                'currency_digits', currency_format.currency_digits)
            kwargs.setdefault(
                'decimal_quantization', currency_format.decimal_quantization)
        return babel_numbers.format_currency(
            self.amount, self.currency, locale=locale, format_type=format_type,
            **kwargs)

    @classmethod
    def parse(cls, s):
        """Parse from a string representation (repr)"""
        try:
            s = s.replace(',', '')
            currency, amount = s.strip().split(' ')
            return cls(amount, currency)
        except ValueError as err:
            raise ValueError(
                "failed to parse string '{}': {}".format(s, err)) from None

@implementer(IPrice)
@attr.s(frozen=True, hash=False, cmp=False, repr=False)
class XPrice(Price):
    """Price subclass with implicit currency conversion"""

    def __add__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__add__(other)

    def __sub__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__sub__(other)

    def __div__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__div__(other)

    def __truediv__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__truediv__(other)

    def __floordiv__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__floordiv__(other)

    def __divmod__(self, other):
        if isinstance(other, Price):
            other = other.to(self.currency)
        return super(XPrice, self).__divmod__(other)


from __future__ import unicode_literals, absolute_import

import json
import numbers

import contextlib2

def coerce(value):
	"""
	coerce takes a value and attempts to convert it to a float,
	or int.

	If none of the conversions are successful, the original value is
	returned.

	>>> coerce('3')
	3

	>>> coerce('3.0')
	3.0

	>>> coerce('foo')
	'foo'

	>>> coerce({})
	{}

	>>> coerce('{}')
	'{}'
	"""
	with contextlib2.suppress(Exception):
		loaded = json.loads(value)
		assert isinstance(loaded, numbers.Number)
		return loaded
	return value


"""
pricing.exchange
~~~~~~~~~~~~~~

Price exchange related classes.

:copyright: (c) 2018 by Joseph Black.
:license: MIT, see LICENSE for more details.
"""

from decimal import Decimal
from datetime import timedelta
import importlib
from typing import ClassVar

from zope.interface import implementer
import attr
from attr.validators import instance_of
import requests
import zulu

from .interfaces import IExchangeBackend, IExchange
from .exceptions import ExchangeBackendNotInstalled

__all__ = ['BackendBase', 'SimpleBackend', 'CoinBaseBackend', 'Exchange']

def ensure_fresh_rates(func):
    """Decorator for Backend that ensures rates are fresh within last 5 mins"""
    def wrapper(self, *args, **kwargs):
        if self.last_updated + timedelta(minutes=5) < zulu.now():
            self.refresh()
        return func(self, *args, **kwargs)
    return wrapper

class BackendBase:
    """Base class API for exchange backends"""

    def quotation(self, origin, target):
        """Return quotation between two currencies (origin, target)"""
        a = self.rate(origin)
        b = self.rate(target)
        if a and b:
            return Decimal(b) / Decimal(a)
        return None

@implementer(IExchangeBackend)
@attr.s
class SimpleBackend(BackendBase):
    """Simple Backend implementation.

    :param base str: An ISO4217 currency code.
    :return: A `SimpleBackend` object.
    :rtype: :inst:`SimpleBackend`

    Usage::

        >>> SimpleBackend(base='USD')
        SimpleBackend(base='USD')

    """

    base: str = attr.ib(default='USD', validator=instance_of(str))
    _rates: dict = attr.ib(init=False, repr=False, factory=dict,
                           validator=instance_of(dict))

    def setrate(self, currency, rate):
        """Sets the rate for currency to provided rate."""
        if not self.base:
            raise Warning("set the base first: backend.base = currency")
        self._rates[currency] = rate

    def rate(self, currency):
        """Returns the rate of exchange from base -> currency."""
        if currency == self.base:
            rate = 1
        else:
            rate = self._rates.get(currency, None)
        if rate:
            return Decimal(rate)

    def quotation(self, origin, target):
        """Returns the rate of exchange from origin -> target currency."""
        return super(SimpleBackend, self).quotation(origin, target)

@implementer(IExchangeBackend)
@attr.s
class CoinBaseBackend(BackendBase):
    """Backend implementation that uses the Coinbase API for rates.

    :param base str: An ISO4217 currency code.
    :return: An `CoinBaseBackend` object.
    :rtype: :inst:`CoinBaseBackend`

    Usage::

        >>> CoinBaseBackend(base='USD')
        CoinBaseBackend(base='USD', last_updated=<Zulu [...]>)

    """

    base: str = attr.ib(default='USD', validator=instance_of(str))

    _rates: dict = attr.ib(repr=False, init=False, factory=dict)
    _headers: ClassVar[dict] = {
        'Accept': 'application/json', 'Content-Type': 'application/json'}
    _base_url: ClassVar[str] = 'https://api.coinbase.com/v2'
    last_updated: zulu.Zulu = attr.ib(init=False)

    @last_updated.default
    def last_updated_default(self):
        now = zulu.now()
        return now - timedelta(minutes=5)

    def _rates_refresh(self, values=None):
        if values:
            base_url = values['_base_url']
            base = values['base']
            headers = values['_headers']
        else:
            base_url = self._base_url
            base = self.base
            headers = self._headers

        url = base_url + '/exchange-rates?currency={}'.format(base)
        r = requests.get(url, headers=headers)
        r.raise_for_status()
        return r.json()['data']['rates']

    def refresh(self):
        """Refresh rates and update last_updated timestamp."""
        self._rates = self._rates_refresh()
        self.last_updated = zulu.now()

    @ensure_fresh_rates
    def rate(self, currency):
        """Returns the rate of exchange from base -> currency."""
        if currency == self.base:
            rate = 1
        else:
            rate = self._rates.get(currency, None)
        if rate:
            return Decimal(rate)

    @ensure_fresh_rates
    def quotation(self, origin, target):
        """Returns the rate of exchange from origin -> target currency."""
        return super(CoinBaseBackend, self).quotation(origin, target)

@implementer(IExchange)
@attr.s
class Exchange:
    """Currency rate exchange class.

    :param _backend IExchangeBackend:
        An instance of an ExchangeBackend providing IExchangeBackend interface.
    :return: An `Exchange` object.
    :rtype: :inst:`Exchange`

    Usage::

        >>> backend = CoinBaseBackend(base='USD')
        ... exchange = Exchange(backend=backend)
        ... exchange
        Exchange(_backend=CoinBaseBackend(
        ... base='USD', last_updated=<Zulu [...]>))

        >>> exchange.rate('EUR')
        Decimal('...')
        ... exchange.quotation('EUR', 'GBP')
        Decimal('...')
    """

    _backend: IExchangeBackend = attr.ib(default=None)

    def __nonzero__(self):
        return self.__bool__()

    def __bool__(self):
        return bool(self._backend)

    def install(self, backend='pricing.exchange.SimpleBackend'):
        """Install an exchange rates backend using a python path string"""

        if isinstance(backend, str):
            path, name = backend.rsplit('.', 1)
            module = importlib.import_module(path)
            backend = getattr(module, name)()
        elif isinstance(backend, type):
            backend = backend()
        if not isinstance(backend, BackendBase):
            raise TypeError("backend '{}' is not a subclass of "
                            "pricing.exchange.BackendBase".format(backend))
        self._backend = backend

    def uninstall(self):
        """Uninstall any exchange rates backend"""
        self._backend = None

    @property
    def backend_name(self):
        """Return the class name of the currently installed backend or None."""
        if not self._backend:
            return None
        return self._backend.__class__.__name__

    @property
    def base(self):
        """Return the base currency."""
        if not self._backend:
            raise ExchangeBackendNotInstalled()
        return self._backend.base

    def rate(self, currency):
        """Returns the rate of exchange from base -> currency."""
        if not self._backend:
            raise ExchangeBackendNotInstalled()
        return self._backend.rate(currency)

    def quotation(self, origin, target):
        """Returns the rate of exchange from origin -> target currency."""
        if not self._backend:
            raise ExchangeBackendNotInstalled()
        return self._backend.quotation(origin, target)

    def __getattr__(self, key):
        return getattr(self._backend, key)

    def __setattr__(self, key, value):
        if key == '_backend':
            self.__dict__[key] = value
        elif self._backend is None:
            raise ExchangeBackendNotInstalled()
        else:
            setattr(self._backend, key, value)


"""
pricing.babel_numbers
~~~~~~~~~~~~~~~~

Custom patches babel.numbers with improved `format_currency` function.

:copyright: (c) 2018 by Joseph Black.
:license: MIT, see LICENSE for more details.
"""

import decimal

import babel.numbers
from babel.core import Locale
from babel.numbers import NumberPattern as _NumberPattern
from babel.numbers import LC_NUMERIC, number_re, UnknownCurrencyFormatError

__all__ = ['format_currency']

def format_currency(number, currency, format=None,
                    locale=LC_NUMERIC, currency_digits=True,
                    format_type='standard', decimal_quantization=True):

    """Return formatted currency value.

    >>> format_currency(1099.98, 'USD', locale='en_US')
    u'$1,099.98'
    >>> format_currency(1099.98, 'USD', locale='es_CO')
    u'US$\\xa01.099,98'
    >>> format_currency(1099.98, 'EUR', locale='de_DE')
    u'1.099,98\\xa0\\u20ac'

    The format can also be specified explicitly.  The currency is
    placed with the '¤' sign.  As the sign gets repeated the format
    expands (¤ being the symbol, ¤¤ is the currency abbreviation and
    ¤¤¤ is the full name of the currency):

    >>> format_currency(1099.98, 'EUR', u'\xa4\xa4 #,##0.00', locale='en_US')
    u'EUR 1,099.98'
    >>> format_currency(1099.98, 'EUR', u'#,##0.00 \xa4\xa4\xa4',
    ...                 locale='en_US')
    u'1,099.98 euros'

    Currencies usually have a specific number of decimal digits. This function
    favours that information over the given format:

    >>> format_currency(1099.98, 'JPY', locale='en_US')
    u'\\xa51,100'
    >>> format_currency(1099.98, 'COP', u'#,##0.00', locale='es_ES')
    u'1.100'

    However, the number of decimal digits can be overriden from the currency
    information, by setting the last parameter to ``False``:

    >>> format_currency(1099.98, 'JPY', locale='en_US', currency_digits=False)
    u'\\xa51,099.98'
    >>> format_currency(1099.98, 'COP', u'#,##0.00', locale='es_ES',
    ...                 currency_digits=False)
    u'1.099,98'

    If a format is not specified the type of currency format to use
    from the locale can be specified:

    >>> format_currency(1099.98, 'EUR', locale='en_US', format_type='standard')
    u'\\u20ac1,099.98'

    When the given currency format type is not available, an exception is
    raised:

    >>> format_currency('1099.98', 'EUR', locale='root', format_type='unknown')
    Traceback (most recent call last):
        ...
    UnknownCurrencyFormatError: "'unknown' is not a known currency format type"

    By default the locale is allowed to truncate and round a high-precision
    number by forcing its format pattern onto the decimal part. You can bypass
    this behavior with the `decimal_quantization` parameter:

    >>> format_currency(1099.9876, 'USD', locale='en_US')
    u'$1,099.99'
    >>> format_currency(1099.9876, 'USD', locale='en_US',
    ...                 decimal_quantization=False)
    u'$1,099.9876'

    :param number: the number to format
    :param currency: the currency code
    :param format: the format string to use
    :param locale: the `Locale` object or locale identifier
    :param currency_digits: use the currency's natural number of decimal digits
    :param format_type: the currency format type to use
    :param decimal_quantization: Truncate and round high-precision numbers to
                                 the format pattern. Defaults to `True`.
    """

    locale = Locale.parse(locale)
    if format:
        pattern = parse_pattern(format)
    else:
        try:
            p = locale.currency_formats[format_type]
            pattern = NumberPattern(
                p.pattern, p.prefix, p.suffix, p.grouping, p.int_prec,
                p.frac_prec, p.exp_prec, p.exp_plus)

        except KeyError:
            raise UnknownCurrencyFormatError(
                "%r is not a known currency format type" % format_type)
    return pattern.apply(
        number, locale, currency=currency, currency_digits=currency_digits,
        decimal_quantization=decimal_quantization)

def parse_pattern(pattern):
    """Parse number format patterns"""
    if isinstance(pattern, NumberPattern):
        return pattern

    def _match_number(pattern):
        rv = number_re.search(pattern)
        if rv is None:
            raise ValueError('Invalid number pattern %r' % pattern)
        return rv.groups()

    pos_pattern = pattern

    # Do we have a negative subpattern?
    if ';' in pattern:
        pos_pattern, neg_pattern = pattern.split(';', 1)
        pos_prefix, number, pos_suffix = _match_number(pos_pattern)
        neg_prefix, _, neg_suffix = _match_number(neg_pattern)
    else:
        pos_prefix, number, pos_suffix = _match_number(pos_pattern)
        neg_prefix = '-' + pos_prefix
        neg_suffix = pos_suffix
    if 'E' in number:
        number, exp = number.split('E', 1)
    else:
        exp = None
    if '@' in number:
        if '.' in number and '0' in number:
            raise ValueError('Significant digit patterns can not contain '
                             '"@" or "0"')
    if '.' in number:
        integer, fraction = number.rsplit('.', 1)
    else:
        integer = number
        fraction = ''

    def parse_precision(p):
        """Calculate the min and max allowed digits"""
        min = max = 0
        for c in p:
            if c in '@0':
                min += 1
                max += 1
            elif c == '#':
                max += 1
            elif c == ',':
                continue
            else:
                break
        return min, max

    int_prec = parse_precision(integer)
    frac_prec = parse_precision(fraction)
    if exp:
        exp_plus = exp.startswith('+')
        exp = exp.lstrip('+')
        exp_prec = parse_precision(exp)
    else:
        exp_plus = None
        exp_prec = None
    grouping = babel.numbers.parse_grouping(integer)
    return NumberPattern(pattern, (pos_prefix, neg_prefix),
                         (pos_suffix, neg_suffix), grouping,
                         int_prec, frac_prec,
                         exp_prec, exp_plus)

class NumberPattern(_NumberPattern):
    """Overriding babel.numbers.NumberPattern.apply to newer version."""

    def apply(
            self, value, locale, currency=None, currency_digits=True,
            decimal_quantization=True):
        """Renders into a string a number following the defined pattern.
        Forced decimal quantization is active by default so we'll produce a
        number string that is strictly following CLDR pattern definitions.
        """
        if not isinstance(value, decimal.Decimal):
            value = decimal.Decimal(str(value))

        value = value.scaleb(self.scale)

        # Separate the absolute value from its sign.
        is_negative = int(value.is_signed())
        value = abs(value).normalize()

        # Prepare scientific notation metadata.
        if self.exp_prec:
            value, exp, exp_sign = self.scientific_notation_elements(
                value, locale)

        # Adjust the precision of the fractionnal part and force it to the
        # currency's if neccessary.
        frac_prec = self.frac_prec
        if currency and currency_digits:
            frac_prec = (babel.numbers.get_currency_precision(currency), ) * 2

        # Bump decimal precision to the natural precision of the number if it
        # exceeds the one we're about to use. This adaptative precision is only
        # triggered if the decimal quantization is disabled or if a scientific
        # notation pattern has a missing mandatory fractional part (as in the
        # default '#E0' pattern). This special case has been extensively
        # discussed at
        # https://github.com/python-babel/babel/pull/494#issuecomment-307649969
        if not decimal_quantization or (self.exp_prec and frac_prec == (0, 0)):
            frac_prec = (frac_prec[0], max([frac_prec[1],
                                            get_decimal_precision(value)]))

        # Render scientific notation.
        if self.exp_prec:
            number = ''.join([
                self._quantize_value(value, locale, frac_prec),
                babel.numbers.get_exponential_symbol(locale),
                exp_sign,
                self._format_int(
                    str(exp), self.exp_prec[0], self.exp_prec[1], locale)])

        # Is it a siginificant digits pattern?
        elif '@' in self.pattern:
            text = self._format_significant(value,
                                            self.int_prec[0],
                                            self.int_prec[1])
            a, sep, b = text.partition(".")
            number = self._format_int(a, 0, 1000, locale)
            if sep:
                number += babel.numbers.get_decimal_symbol(locale) + b

        # A normal number pattern.
        else:
            number = self._quantize_value(value, locale, frac_prec)

        retval = ''.join([
            self.prefix[is_negative],
            number,
            self.suffix[is_negative]])

        if u'¤' in retval:
            retval = retval.replace(u'¤¤¤',
                                    babel.numbers.get_currency_name(
                                        currency, value, locale))
            retval = retval.replace(u'¤¤', currency.upper())
            retval = retval.replace(u'¤', babel.numbers.get_currency_symbol(
                currency, locale))

        return retval

    def _quantize_value(self, value, locale, frac_prec):
        quantum = get_decimal_quantum(frac_prec[1])
        rounded = value.quantize(quantum)
        a, sep, b = str(rounded).partition(".")
        number = (self._format_int(a, self.int_prec[0],
                                   self.int_prec[1], locale) +
                  self._format_frac(b or '0', locale, frac_prec))
        return number

    def scientific_notation_elements(self, value, locale):
        """ Returns normalized scientific notation components of a value."""
        # Normalize value to only have one lead digit.
        exp = value.adjusted()
        value = value * get_decimal_quantum(exp)
        assert value.adjusted() == 0

        # Shift exponent and value by the minimum number of leading digits
        # imposed by the rendering pattern. And always make that number
        # greater or equal to 1.
        lead_shift = max([1, min(self.int_prec)]) - 1
        exp = exp - lead_shift
        value = value * get_decimal_quantum(-lead_shift)

        # Get exponent sign symbol.
        exp_sign = ''
        if exp < 0:
            exp_sign = babel.numbers.get_minus_sign_symbol(locale)
        elif self.exp_plus:
            exp_sign = babel.numbers.get_plus_sign_symbol(locale)

        # Normalize exponent value now that we have the sign.
        exp = abs(exp)

        return value, exp, exp_sign

def get_decimal_quantum(precision):
    """Return minimal quantum of a number, as defined by precision."""
    assert isinstance(precision, (int, decimal.Decimal))
    return decimal.Decimal(10) ** (-precision)

def get_decimal_precision(number):
    """Return maximum precision of a decimal instance's fractional part.
    Precision is extracted from the fractional part only.
    """
    # Copied from: https://github.com/mahmoud/boltons/pull/59
    assert isinstance(number, decimal.Decimal)
    decimal_tuple = number.normalize().as_tuple()
    if decimal_tuple.exponent >= 0:
        return 0
    return abs(decimal_tuple.exponent)


"""
Simple module for simulating dice rolls.
"""

from __future__ import print_function, absolute_import, unicode_literals

import random
from argparse import ArgumentParser

def get_options():
	parser = ArgumentParser()
	parser.add_argument('-s', '--sides', default=6, type=int)
	parser.add_argument('number', nargs='?', type=int, default=1)
	args = parser.parse_args()
	return args

class Dice(object):
	"""
	>>> d20 = Dice(20)
	>>> 1 <= d20.roll() <= 20
	True
	"""
	def __init__(self, sides):
		self.sides = sides

	def roll(self):
		return random.randint(1, self.sides)

def do_dice_roll():
	"""
	Roll n-sided dice and return each result and the total
	"""
	options = get_options()
	dice = Dice(options.sides)
	rolls = [dice.roll() for n in range(options.number)]
	for roll in rolls:
		print('rolled', roll)
	if options.number > 1:
		print('total', sum(rolls))


"""
pricing.fields
~~~~~~~~~~~~

Fields helpers for zope.schema and attrs.

:copyright: (c) 2018 by Joseph Black.
:license: MIT, see LICENSE for more details.
"""

from zope.interface import implementer
from zope.schema.interfaces import IFromUnicode
from zope.schema._field import NativeStringLine

import attr
from attr.validators import instance_of
from .price import Price as PriceClass

__all__ = ['Price', 'price']

def price_converter(obj):
    """Ensures that string prices are converted into Price objects."""
    if isinstance(obj, str):
        obj = PriceClass.parse(obj)
    return obj

@implementer(IFromUnicode)
class Price(NativeStringLine):
    """Price field for zope.schema.
    """

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('title', 'Price')
        kwargs.setdefault(
            'description', 'A price object')
        kwargs.setdefault('required', False)
        kwargs.setdefault('readonly', False)
        kwargs.setdefault('default', 'USD 0.00')
        super(Price, self).__init__(*args, **kwargs)

    def _validate(self, value):
        super(Price, self)._validate(value)

    def fromUnicode(self, value):
        """See IFromUnicode."""
        v = value.strip()
        if isinstance(v, bytes):
            v = v.decode()
        v = PriceClass.parse(value)
        self.validate(v)
        return v

def price(*args, **kwargs):
    """Price field for attrs.

    See `help(attr.ib)` for full signature.

    Usage:

        >>> from pricing import fields
        ... @attr.s
        ... class Test:
        ...     price: Price = fields.price(default='USD 5.00')
        ...
        ... Test()
        Test(price=USD 5.00)
    """

    kwargs.setdefault('default', 'USD 0.00')
    kwargs.setdefault('converter', price_converter)

    if 'validator' in kwargs:
        validator = kwargs.pop('validator')
        if not isinstance(validator, (tuple, list)):
            validator = [validator]
    else:
        validator = []
    validator.append(instance_of(PriceClass))
    return attr.ib(validator=validator, *args, **kwargs)


"""
    service_factory.service
    ~~~~~~~~~~~~~~~~~~~~~~~

    This module define service class.

    :copyright: (c) 2015-2016 by Artem Malyshev.
    :license: GPL3, see LICENSE for more details.
"""

from __future__ import (
    absolute_import, unicode_literals, division, print_function,
)
from json import loads, dumps

from .errors import (
    invalid_request, method_not_found, parse_error, server_error)
from .validation import (
    validate_version, validate_method, validate_params, validate_id)

class Service(object):
    """Base Service.  Provide application method access."""

    def __init__(self, app):
        """Service constructor.

        :param app: application definition
        :type app: list of callable, dict of callable

        """

        if isinstance(app, list):
            self.app = dict((method.__name__, method) for method in app)
        elif isinstance(app, dict):
            self.app = app

    def __call__(self, arg):
        """Perform jsonrpc call.

        :param arg: JSON-RPC request body
        :type arg: str
        :raises: ServiceException

        """

        args = self.load_args(arg)
        self.validate(args)
        method = self.get_method(args)
        result = self.apply(method, args)
        response = self.make_response(args, result)
        return 200, response

    def load_args(self, arg):
        """Loads service args from string.

        :param arg: Request body
        :raises: ServiceException

        """

        try:
            args = loads(arg)
        except ValueError:
            parse_error()
        else:
            return args

    def validate(self, request):
        """Validate JSON-RPC request.

        :param request: RPC request object
        :type request: dict

        """

        try:
            validate_version(request)
            validate_method(request)
            validate_params(request)
            validate_id(request)
        except (AssertionError, KeyError) as error:
            invalid_request(error)

    def get_method(self, args):
        """Get request method for service application."""

        try:
            method = self.app[args['method']]
        except KeyError:
            method_not_found(args['id'])
        else:
            return method

    def apply(self, method, args):
        """Apply application method."""

        try:
            params = args['params']
            if isinstance(params, dict):
                result = method(**params)
            else:
                result = method(*params)
        except Exception as error:
            server_error(args['id'], error)
        else:
            return result

    def make_response(self, args, result):
        """Create response body from given result."""

        return dumps({
            'jsonrpc': '2.0',
            'id': args['id'],
            'result': result,
        })


from django.core import serializers
from django.db import models
from django.forms.models import model_to_dict
from django.http import HttpResponse

class JsonResponseEncoder(serializers.json.DjangoJSONEncoder):
    """
    JSON encoder that converts Django types to JSON.
    """
    def default(self, obj):
        """
        Convert QuerySet objects to their list counter-parts
        """
        if isinstance(obj, models.Model):
            return self.encode(model_to_dict(obj))
        elif isinstance(obj, models.query.QuerySet):
            return serializers.serialize('json', obj)
        else:
            return super(JsonResponseEncoder, self).default(obj)

class JsonResponse(HttpResponse):
    def __init__(self, data={}):
        super(JsonResponse, self).__init__(content_type='application/json')
        self.set_content(data)

    def set_content(self, data):
        json_encoder = JsonResponseEncoder(separators=(',', ':'))
        content = json_encoder.encode(data)
        self.content = content

class JsonResponseCreated(JsonResponse):
    status_code = 201

class JsonResponseAccepted(JsonResponse):
    status_code = 202

class JsonResponseBadRequest(JsonResponse):
    status_code = 400

class JsonResponseWithStatus(JsonResponse):
    def __init__(self, error_message='', error_type=None, field_errors=None):
        data = {
            'error_type': error_type or self.status_code,
            'error_message': error_message
        }
        if field_errors:
            data['field_errors'] = field_errors
        super(JsonResponseWithStatus, self).__init__(data)

class JsonResponseSeeOther(JsonResponseWithStatus):
    status_code = 302

class JsonResponseForbidden(JsonResponseWithStatus):
    status_code = 403

class JsonResponseConflict(JsonResponseWithStatus):
    status_code = 409

class JsonResponseError(JsonResponseWithStatus):
    status_code = 500

class JsonResponseUnauthorized(JsonResponseWithStatus):
    status_code = 401

class JsonResponseNotFound(JsonResponseWithStatus):
    status_code = 404

class JsonResponseNotSupported(JsonResponseWithStatus):
    status_code = 400


import json
import logging
from functools import wraps
from django import forms
from django.conf import settings
from django.db import models
from django_api.json_helpers import JsonResponse
from django_api.json_helpers import JsonResponseBadRequest
from django_api.json_helpers import JsonResponseNotFound

logger = logging.getLogger(__name__)

class ValidatedRequest(object):
    """
    A wrapper for Request objects that behaves like a Request in every way
    except that GET/POST data is replaced with a cleaned version (per Django
    forms).

    See the @api decorator for more details.
    """

    def __init__(self, orig_request, form):
        self._orig_request = orig_request
        self._form = form

    def __getattr__(self, name):
        if name in ['GET', 'POST']:
            return self._form.cleaned_data
        else:
            return object.__getattribute__(self._orig_request, name)

def api_accepts(fields):
    """
    Define the accept schema of an API (GET or POST).

    'fields' is a dict of Django form fields keyed by field name that specifies
    the form-urlencoded fields that the API accepts*.

    The view function is then called with GET/POST data that has been cleaned
    by the Django form.

    In debug and test modes, failure to validate the fields will result in a
    400 Bad Request response.
    In production mode, failure to validate will just log a
    warning, unless overwritten by a 'strict' setting.

    For example:

    @api_accepts({
        'x': forms.IntegerField(min_value=0),
        'y': forms.IntegerField(min_value=0),
    })
    def add(request, *args, **kwargs):
        x = request.POST['x']
        y = request.POST['y']

        # x and y are integers already.
        return HttpResponse('%d' % (x + y))

    *: 'fields' can also include Django models as {'key': Model()}. If present,
    api_accepts will look for the field keyed by '<key>-id'
    and pick the object that has that primary key. For example, if the entry is
    {'course': Course()}, it will search for the key course_id='course-id' in
    the request object, and find the object Course.objects.get(pk=course_id)
    """
    def decorator(func):
        @wraps(func)
        def wrapped_func(request, *args, **kwargs):
            if request.method not in ['GET', 'POST']:
                return func(request, *args, **kwargs)

            # The fields dict passed into the type() function is modified, so
            # send in a copy instead.
            form_class = type('ApiForm', (forms.Form,), fields.copy())
            form = form_class(getattr(request, request.method))

            if not form.is_valid():
                if settings.DEBUG:
                    return JsonResponseBadRequest(
                        'failed to validate: %s' % dict(form.errors)
                    )
                else:
                    logger.warn(
                        'input to \'%s\' failed to validate: %s',
                        request.path,
                        dict(form.errors)
                    )
                    return func(request, *args, **kwargs)

            # Clean any models.Model fields, by looking up object based on
            # primary key in request.
            for (field_name, field_instance) in fields.items():
                if isinstance(field_instance, models.Model):
                    field_type = type(field_instance)
                    # TODO: irregular, should we remove?
                    field_id = '%s-id' % field_name
                    if field_id not in request.REQUEST:
                        return JsonResponseBadRequest(
                            'field %s not present' % field_name
                        )
                    field_pk = int(request.REQUEST[field_id])
                    try:
                        field_value = field_type.objects.get(pk=field_pk)
                    except field_type.DoesNotExist:
                        return JsonResponseNotFound(
                            '%s with pk=%d does not exist' % (
                                field_type, field_pk
                            )
                        )
                    form.cleaned_data[field_name] = field_value

            validated_request = ValidatedRequest(request, form)
            return func(validated_request, *args, **kwargs)
        return wrapped_func
    return decorator

def api_returns(return_values):
    """
    Define the return schema of an API.

    'return_values' is a dictionary mapping
    HTTP return code => documentation
    In addition to validating that the status code of the response belongs to
    one of the accepted status codes, it also validates that the returned
    object is JSON (derived from JsonResponse)

    In debug and test modes, failure to validate the fields will result in a
    400 Bad Request response.
    In production mode, failure to validate will just log a
    warning, unless overwritten by a 'strict' setting.

    For example:

    @api_returns({
        200: 'Operation successful',
        403: 'User does not have persion',
        404: 'Resource not found',
        404: 'User not found',
    })
    def add(request, *args, **kwargs):
        if not request.user.is_superuser:
            return JsonResponseForbidden()  # 403

        return HttpResponse()  # 200
    """
    def decorator(func):
        @wraps(func)
        def wrapped_func(request, *args, **kwargs):
            return_value = func(request, *args, **kwargs)

            if not isinstance(return_value, JsonResponse):
                if settings.DEBUG:
                    return JsonResponseBadRequest('API did not return JSON')
                else:
                    logger.warn('API did not return JSON')

            accepted_return_codes = return_values.keys()
            # Never block 500s - these should be handled by other
            # reporting mechanisms
            accepted_return_codes.append(500)

            if return_value.status_code not in accepted_return_codes:
                if settings.DEBUG:
                    return JsonResponseBadRequest(
                        'API returned %d instead of acceptable values %s' %
                        (return_value.status_code, accepted_return_codes)
                    )
                else:
                    logger.warn(
                        'API returned %d instead of acceptable values %s',
                        return_value.status_code,
                        accepted_return_codes,
                    )

            return return_value
        return wrapped_func
    return decorator

def api(accept_return_dict):
    """
    Wrapper that calls @api_accepts and @api_returns in sequence.
    For example:

    @api({
        'accepts': {
            'x': forms.IntegerField(min_value=0),
            'y': forms.IntegerField(min_value=0),
        },
        'returns': [
            200: 'Operation successful',
            403: 'User does not have persion',
            404: 'Resource not found',
            404: 'User not found',
        ]
    })
    def add(request, *args, **kwargs):
        if not request.GET['x'] == 10:
            return JsonResponseForbidden()  # 403

        return HttpResponse()  # 200
    """
    def decorator(func):
        @wraps(func)
        def wrapped_func(request, *args, **kwargs):
            @api_accepts(accept_return_dict['accepts'])
            @api_returns(accept_return_dict['returns'])
            def apid_fnc(request, *args, **kwargs):
                return func(request, *args, **kwargs)

            return apid_fnc(request, *args, **kwargs)
        return wrapped_func
    return decorator

def validate_json_request(required_fields):
    """
    Return a decorator that ensures that the request passed to the view
    function/method has a valid JSON request body with the given required
    fields.  The dict parsed from the JSON is then passed as the second
    argument to the decorated function/method.  For example:

    @json_request({'name', 'date'})
    def view_func(request, request_dict):
        ...
    """
    def decorator(func):
        @wraps(func)
        def wrapped_func(request, *args, **kwargs):
            try:
                request_dict = json.loads(request.raw_post_data)
            except ValueError as e:
                return JsonResponseBadRequest('invalid POST JSON: %s' % e)

            for k in required_fields:
                if k not in request_dict:
                    return JsonResponseBadRequest(
                        'POST JSON must contain property \'%s\'' % k)

            return func(request, request_dict, *args, **kwargs)
        return wrapped_func
    return decorator


# -*- coding: utf-8 -*-
from __future__ import unicode_literals, division, print_function, absolute_import
import re
import os
import unittest
import sys
import inspect
import importlib
import logging
import hashlib

from .compat import *
from .utils import modname

logger = logging.getLogger(__name__)

class RerunFile(object):
    def __init__(self):
        import tempfile
        self.filepath = os.path.join(tempfile.gettempdir(), "{}.txt".format(modname()))

    def __enter__(self):
        self.fp = open(self.filepath, encoding="utf-8", mode="w+")
        return self

    def writeln(self, s):
        self.fp.write(s)
        self.fp.write("\n")

    def __exit__(self, exception_type, exception_value, traceback):
        self.fp.close()
        self.fp = None

    def read(self):
        with open(self.filepath, encoding="utf-8", mode="r+") as fp:
            return fp.read()

    def __iter__(self):
        with open(self.filepath, encoding="utf-8", mode="r+") as fp:
            for line in fp:
                yield line.strip()

class PathGuesser(object):
    """PathGuesser

    This class compiles the possible paths, it is created in the TestLoader and then
    the .possible attribute is iterated to actually load the tests.

    The .possible property consists of PathFinder objects

    https://docs.python.org/3/library/unittest.html#test-discovery
    """
    def __init__(self, name, basedir="", method_prefix='test', **kwargs):
        self.name = name
        if not basedir:
            basedir = os.getcwd()
        self.basedir = basedir
        self.method_prefix = method_prefix
        self.set_possible()

    def raise_any_error(self):
        """raise any found error in the possible PathFinders"""
        for tc in self.possible:
            tc.raise_found_error()

    def set_possible(self):
        '''
        break up a module path to its various parts (prefix, module, class, method)

        this uses PEP 8 conventions, so foo.Bar would be foo module with class Bar

        return -- list -- a list of possible interpretations of the module path
            (eg, foo.bar can be bar module in foo module, or bar method in foo module)
        '''
        possible = []
        name = self.name
        logger.debug('Guessing test name: {}'.format(name))

        name_f = self.name.lower()
        filepath = ""
        if name_f.endswith(".py") or ".py:" in name_f:
            # path/something:Class.method
            bits = name.split(":", 1)
            filepath = bits[0]
            logger.debug('Found filepath: {}'.format(filepath))

            name = bits[1] if len(bits) > 1 else ""
            if name:
                logger.debug('Found test name: {} for filepath: {}'.format(name, filepath))

        bits = name.split('.')
        basedir = self.basedir
        method_prefix = self.method_prefix

        # check if the last bit is a Class
        if re.search(r'^\*?[A-Z]', bits[-1]):
            logger.debug('Found class in name: {}'.format(bits[-1]))
            possible.append(PathFinder(basedir, method_prefix, **{
                'class_name': bits[-1],
                'module_name': bits[-2] if len(bits) > 1 else '',
                'prefix': os.sep.join(bits[0:-2]),
                'filepath': filepath,
            }))
        elif len(bits) > 1 and re.search(r'^\*?[A-Z]', bits[-2]):
            logger.debug('Found class in name: {}'.format(bits[-2]))
            possible.append(PathFinder(basedir, method_prefix, **{
                'class_name': bits[-2],
                'method_name': bits[-1],
                'module_name': bits[-3] if len(bits) > 2 else '',
                'prefix': os.sep.join(bits[0:-3]),
                'filepath': filepath,
            }))
        else:
            if self.name:
                if filepath:
                    if len(bits):
                        possible.append(PathFinder(basedir, method_prefix, **{
                            'filepath': filepath,
                            'method_name': bits[0],
                        }))
                    else:
                        possible.append(PathFinder(basedir, method_prefix, **{
                            'filepath': filepath,
                        }))

                else:
                    logger.debug('Test name is ambiguous')
                    possible.append(PathFinder(basedir, method_prefix, **{
                        'module_name': bits[-1],
                        'prefix': os.sep.join(bits[0:-1]),
                        'filepath': filepath,
                    }))
                    possible.append(PathFinder(basedir, method_prefix, **{
                        'method_name': bits[-1],
                        'module_name': bits[-2] if len(bits) > 1 else '',
                        'prefix': os.sep.join(bits[0:-2]),
                        'filepath': filepath,
                    }))
                    possible.append(PathFinder(basedir, method_prefix, **{
                        'prefix': os.sep.join(bits),
                        'filepath': filepath,
                    }))

            else:
                possible.append(PathFinder(basedir, method_prefix, filepath=filepath))

        logger.debug("Found {} possible test names".format(len(possible)))
        self.possible = possible

class PathFinder(object):
    """Pathfinder class

    this is where all the magic happens, PathGuesser guesses on what the paths might
    be and creates instances of this class, those instances then actually validate
    the guesses and allow the tests to be loaded or not
    """
    def __init__(self, basedir, method_prefix='test', **kwargs):
        self.basedir = basedir
        self.method_prefix = method_prefix
        self.module_prefixes = ["test_", "test"]
        self.module_postfixes = ["_test", "test", "_tests", "tests"]
        for k, v in kwargs.items():
            setattr(self, k, v)

    def has_module(self):
        v = getattr(self, 'module_name', None)
        return bool(v)

    def has_class(self):
        v = getattr(self, 'class_name', None)
        return bool(v)

    def has_method(self):
        v = getattr(self, 'method_name', None)
        return bool(v)

    def __str__(self):
        ret = ''
        for k in ['prefix', 'module_name', 'class_name', 'method_name', 'filepath']:
            v = getattr(self, k, None)
            if v:
                ret += "{}: {}, ".format(k, v)

        return ret.rstrip(', ')

    def raise_found_error(self):
        """raise an error if one was found, otherwise do nothing"""
        error_info = getattr(self, 'error_info', None)
        if error_info:

            reraise(*error_info)

    def modules(self):
        """return modules that match module_name"""

        # since the module has to be importable we go ahead and put the
        # basepath as the very first path to check as that should minimize
        # namespace collisions, this is what unittest does also
        sys.path.insert(0, self.basedir)
        for p in self.paths():
            # http://stackoverflow.com/questions/67631/
            try:
                module_name = self.module_path(p)
                logger.debug("Importing {} from path {}".format(module_name, p))
                m = importlib.import_module(module_name)
                yield m

            except Exception as e:
                logger.warning('Caught exception while importing {}: {}'.format(p, e))
                logger.warning(e, exc_info=True)
                error_info = getattr(self, 'error_info', None)
                if not error_info:
                    exc_info = sys.exc_info()
                    #raise e.__class__, e, exc_info[2]
                    #self.error_info = (e, exc_info)
                    self.error_info = exc_info
                continue

        sys.path.pop(0)

    def classes(self):
        """the partial self.class_name will be used to find actual TestCase classes"""
        for module in self.modules():
            cs = inspect.getmembers(module, inspect.isclass)
            class_name = getattr(self, 'class_name', '')
            class_regex = ''
            if class_name:
                if class_name.startswith("*"):
                    class_name = class_name.strip("*")
                    class_regex = re.compile(r'.*?{}'.format(class_name), re.I)
                else:
                    class_regex = re.compile(r'^{}'.format(class_name), re.I)

            for c_name, c in cs:
                can_yield = True
                if class_regex and not class_regex.match(c_name):
                #if class_name and class_name not in c_name:
                    can_yield = False

                if can_yield and issubclass(c, unittest.TestCase):
                    if c is not unittest.TestCase: # ignore actual TestCase class
                        logger.debug('class: {} matches {}'.format(c_name, class_name))
                        yield c

    def method_names(self):
        """return the actual test methods that matched self.method_name"""
        for c in self.classes():
            #ms = inspect.getmembers(c, inspect.ismethod)
            # http://stackoverflow.com/questions/17019949/
            ms = inspect.getmembers(c, lambda f: inspect.ismethod(f) or inspect.isfunction(f))
            method_name = getattr(self, 'method_name', '')
            method_regex = ''
            if method_name:
                if method_name.startswith(self.method_prefix):
                    method_regex = re.compile(r'^{}'.format(method_name), flags=re.I)

                else:

                    if method_name.startswith("*"):
                        method_name = method_name.strip("*")
                        method_regex = re.compile(
                            r'^{}[_]{{0,1}}.*?{}'.format(self.method_prefix, method_name),
                            flags=re.I
                        )
                    else:
                        method_regex = re.compile(
                            r'^{}[_]{{0,1}}{}'.format(self.method_prefix, method_name),
                            flags=re.I
                        )

            for m_name, m in ms:
                if not m_name.startswith(self.method_prefix): continue

                can_yield = True
                if method_regex and not method_regex.match(m_name):
                    can_yield = False

                if can_yield:
                    logger.debug('method: {} matches {}'.format(m_name, method_name))
                    yield c, m_name

    def _find_basename(self, name, basenames, is_prefix=False):
        """check if name combined with test prefixes or postfixes is found anywhere
        in the list of basenames

        :param name: string, the name you're searching for
        :param basenames: list, a list of basenames to check
        :param is_prefix: bool, True if this is a prefix search, which means it will
            also check if name matches any of the basenames without the prefixes or
            postfixes, if it is False then the prefixes or postfixes must be present
            (ie, the module we're looking for is the actual test module, not the parent
             modules it's contained in)
        :returns: string, the basename if it is found
        """
        ret = ""
        fileroots = [(os.path.splitext(n)[0], n) for n in basenames]
        glob = False
        if name.startswith("*"):
            glob = True
        name = name.strip("*")

        for fileroot, basename in fileroots:
            if name in fileroot or fileroot in name:
                for pf in self.module_postfixes:
                    logger.debug(
                        'Checking if basename {} starts with {} and ends with {}'.format(
                        basename,
                        name,
                        pf
                    ))
                    if glob:
                        if name in fileroot and fileroot.endswith(pf):
                            ret = basename
                            break
                    else:
                        if fileroot.startswith(name) and fileroot.endswith(pf):
                            ret = basename
                            break

                if not ret:
                    for pf in self.module_prefixes:
                        n = pf + name
                        logger.debug('Checking if basename {} starts with {}'.format(basename, n))
                        if glob:
                            if fileroot.startswith(pf) and name in fileroot:
                                ret = basename
                                break
                        else:
                            if fileroot.startswith(n):
                                ret = basename
                                break

                if not ret:
                    if is_prefix:
                        logger.debug('Checking if basename {} starts with {}'.format(basename, name))
                        if basename.startswith(name) or (glob and name in basename):
                            ret = basename

                        else:
                            logger.debug(
                                'Checking if basename {} starts with {} and is a test module'.format(
                                basename,
                                name
                            ))
                            if glob:
                                if name in basename and self._is_module_path(basename):
                                    ret = basename

                            else:
                                if basename.startswith(name) and self._is_module_path(basename):
                                    ret = basename

                if ret:
                    logger.debug('Found basename {}'.format(ret))
                    break

        return ret

    def _find_prefix_paths(self, basedir, prefix):
        ret = basedir
        modnames = re.split(r"[\.\/]", prefix)
        seen_paths = set()

        for root, dirs, files in self.walk(basedir):
            logger.debug("Checking {} for prefix {}".format(root, prefix))
            ret = root
            for modname in modnames:
                for root2, dirs2, files2 in self.walk(ret):
                    logger.debug("Checking {} for modname {}".format(root2, modname))
                    ret = ""
                    basename = self._find_basename(modname, dirs2, is_prefix=True)
                    if basename:
                        ret = os.path.join(root2, basename)
                        logger.debug("Found prefix path {}".format(ret))
                        break

                if not ret:
                    logger.debug("Could not find a prefix path in {} matching {}".format(root, modname))
                    break

            if ret:
                if ret not in seen_paths:
                    seen_paths.add(ret)
                    logger.debug("Yielding prefix path {}".format(ret))
                    yield ret

    def _find_prefix_path(self, basedir, prefix):
        """Similar to _find_prefix_paths() but only returns the first match"""
        ret = ""
        for ret in self._find_prefix_paths(basedir, prefix):
            break

        if not ret:
            raise IOError("Could not find prefix {} in path {}".format(prefix, basedir))

        return ret

    def _find_module_path(self, basedir, modname):
        ret = ""

        try:
            ret = self._find_prefix_path(basedir, modname)

        except IOError:
            logger.debug('Checking for a module that matches {} in {}'.format(modname, basedir))
            for root, dirs, files in self.walk(basedir):
                basename = self._find_basename(modname, files, is_prefix=False)
                if basename:
                    ret = os.path.join(root, basename)
                    break

                for basename in files:
                    fileroot = os.path.splitext(basename)[0]
                    if fileroot in modname or modname in fileroot:
                        for pf in self.module_postfixes:
                            n = modname + pf
                            logger.debug('Checking {} against {}'.format(n, fileroot))
                            if fileroot.startswith(n):
                                ret = os.path.join(root, basename)
                                break

                        if not ret:
                            for pf in self.module_prefixes:
                                n = pf + modname
                                logger.debug('Checking {} against {}'.format(n, fileroot))
                                if fileroot.startswith(n):
                                    ret = os.path.join(root, basename)
                                    break

                        if not ret:
                            if self._is_module_path(basename) and modname == basename:
                                ret = os.path.join(root, basename)
                                break

                    if ret: break
                if ret: break

        if not ret:
            raise IOError("Could not find a module path with {}".format(modname))

        logger.debug("Found module path {}".format(ret))
        return ret

    def _is_module_path(self, path):
        """Returns true if the passed in path is a test module path

        :param path: string, the path to check, will need to start or end with the
            module test prefixes or postfixes to be considered valid
        :returns: boolean, True if a test module path, False otherwise
        """
        ret = False
        basename = os.path.basename(path)
        fileroot = os.path.splitext(basename)[0]
        for pf in self.module_postfixes:
            if fileroot.endswith(pf):
                ret = True
                break

        if not ret:
            for pf in self.module_prefixes:
                if fileroot.startswith(pf):
                    ret = True
                    break
        return ret

    def walk(self, basedir):
        """Walk all the directories of basedir except hidden directories

        :param basedir: string, the directory to walk
        :returns: generator, same as os.walk
        """
        system_d = SitePackagesDir()
        filter_system_d = system_d and os.path.commonprefix([system_d, basedir]) != system_d

        for root, dirs, files in os.walk(basedir, topdown=True):
            # ignore dot directories and private directories (start with underscore)
            dirs[:] = [d for d in dirs if d[0] != '.' and d[0] != "_"]

            if filter_system_d:
                dirs[:] = [d for d in dirs if not d.startswith(system_d)]

            yield root, dirs, files

    def paths(self):
        '''
        given a basedir, yield all test modules paths recursively found in
        basedir that are test modules

        return -- generator
        '''
        module_name = getattr(self, 'module_name', '')
        module_prefix = getattr(self, 'prefix', '')
        filepath = getattr(self, 'filepath', '')

        if filepath:
            if os.path.isabs(filepath):
                yield filepath

            else:
                yield os.path.join(self.basedir, filepath)

        else:
            if module_prefix:
                basedirs = self._find_prefix_paths(self.basedir, module_prefix)
            else:
                basedirs = [self.basedir]

            for basedir in basedirs:
                try:
                    if module_name:
                        path = self._find_module_path(basedir, module_name)

                    else:
                        path = basedir

                    if os.path.isfile(path):
                        logger.debug('Module path: {}'.format(path))
                        yield path

                    else:
                        seen_paths = set()
                        for root, dirs, files in self.walk(path):
                            for basename in files:
                                if basename.startswith("__init__"):
                                    if self._is_module_path(root):
                                        filepath = os.path.join(root, basename)
                                        if filepath not in seen_paths:
                                            logger.debug('Module package path: {}'.format(filepath))
                                            seen_paths.add(filepath)
                                            yield filepath

                                else:
                                    fileroot = os.path.splitext(basename)[0]
                                    for pf in self.module_postfixes:
                                        if fileroot.endswith(pf):
                                            filepath = os.path.join(root, basename)
                                            if filepath not in seen_paths:
                                                logger.debug('Module postfix path: {}'.format(filepath))
                                                seen_paths.add(filepath)
                                                yield filepath

                                    for pf in self.module_prefixes:
                                        if fileroot.startswith(pf):
                                            filepath = os.path.join(root, basename)
                                            if filepath not in seen_paths:
                                                logger.debug('Module prefix path: {}'.format(filepath))
                                                seen_paths.add(filepath)
                                                yield filepath

                except IOError as e:
                    # we failed to find a suitable path
                    logger.warning(e, exc_info=True)
                    pass

    def module_path(self, filepath):
        """given a filepath like /base/path/to/module.py this will convert it to
        path.to.module so it can be imported"""
        possible_modbits = re.split('[\\/]', filepath.strip('\\/'))
        basename = possible_modbits[-1]
        prefixes = possible_modbits[0:-1]
        modpath = []
        discarded = []

        # find the first directory that has an __init__.py
        for i in range(len(prefixes)):
            path_args = ["/"]
            path_args.extend(prefixes[0:i+1])
            path_args.append('__init__.py')
            prefix_module = os.path.join(*path_args)
            #logger.debug("Checking prefix modulepath: {}".format(prefix_module))
            if os.path.isfile(prefix_module):
                #logger.debug("Found start of modulepath: {}".format(prefixes[i]))
                modpath = prefixes[i:]
                break

            else:
                discarded = path_args[0:-1]

        modpath.append(basename)

        # convert the remaining file path to a python module path that can be imported
        module_name = '.'.join(modpath)
        module_name = re.sub(r'(?:\.__init__)?\.py$', '', module_name, flags=re.I)
        logger.debug("Module path {} found in filepath {}".format(module_name, filepath))
        return module_name

# !!! Ripped from pout.path
import site
class SitePackagesDir(String):
    """Finds the site-packages directory and sets the value of this string to that
    path"""
    def __new__(cls):
        basepath = ""
        try:
            paths = site.getsitepackages()
            basepath = paths[0]
            logger.debug(
                "Found site-packages directory {} using site.getsitepackages".format(
                    basepath
                )
            )

        except AttributeError:
            # we are probably running this in a virtualenv, so let's try a different
            # approach
            # try and brute-force discover it since it's not defined where it
            # should be defined
            sitepath = os.path.join(os.path.dirname(site.__file__), "site-packages")
            if os.path.isdir(sitepath):
                basepath = sitepath
                logger.debug(
                    "Found site-packages directory {} using site.__file__".format(
                        basepath
                    )
                )

            else:
                for path in sys.path:
                    if path.endswith("site-packages"):
                        basepath = path
                        logger.debug(
                            "Found site-packages directory {} using sys.path".format(
                                basepath
                            )
                        )
                        break

                if not basepath:
                    for path in sys.path:
                        if path.endswith("dist-packages"):
                            basepath = path
                            logger.debug(
                                "Found dist-packages directory {} using sys.path".format(
                                    basepath
                                )
                            )
                            break

        if not basepath:
            raise IOError("Could not find site-packages directory")

        return super(SitePackagesDir, cls).__new__(cls, basepath)


import sys

class Translator(object):

    def __init__(self, max_iterations=10):
        self.de_words = []
        self.de_dict = []
        self.en_words = []
        self.en_dict = []
        self.sent_pairs = []

        self.probs = {}
        self.transmissions = {}  # this is t(e|f)
        self.countef = {}  # this countef
        self.totalf = {}
        self.totals = {}
        self.max_iterations = max_iterations

    def train(self, english, foreign):
        self.en_dict, self.en_words = self.convertArgsToTokens(english)
        self.de_dict, self.de_words = self.convertArgsToTokens(foreign)

        for index in range(len(self.en_dict)):
            pair = (self.en_dict[index], self.de_dict[index])
            self.sent_pairs.append(pair)

        self.initTef()
        self.iterateEM(self.max_iterations)

    def translate(self, word):
        """
        pass in a word string that you
        would like to see probable matches for.
        """
        if (word not in self.transmissions):
            raise NoMatchError('no matches found')
        else:
            trans = self.transmissions[word]
            # print out a sorted list of all non-zero trans
            return sorted(((k, v) for k, v in trans.iteritems() if v != 0),
                                                                reverse=True)

    def convertArgsToTokens(self, data):
        """
        this converts the readin lines from
        sys to useable format, returns list
        of token and dict of tokens
        """

        tdict = []
        tokens = []

        d = open(data, 'r')
        for line in d.readlines():
            tdict.append(line.rstrip())
            tokens += line.split()

        d.close()
        tokens = list(set(tokens))

        return tdict, tokens

    def initTef(self):
        '''
        get all probable matches
        and then initialize t(f|e)
        '''
        probs = {}
        transmissions = {}

        # go through each german word
        for word in self.en_words:
            word_poss = []
            # if word in sentence.. then
            for sent in self.en_dict:
                if word in sent:
                    matching = self.de_dict[self.en_dict.index(sent)]
                    word_poss = word_poss + matching.split()

            # remove the duplicates
            word_poss = list(set(word_poss))
            # add the probable matches
            probs[word] = word_poss

        self.probs = probs
        print self.probs

        for word in self.en_words:
            # print self.probs
            word_probs = self.probs[word]
            if (len(word_probs) == 0):
                print word, word_probs
            uniform_prob = 1.0 / len(word_probs)

            word_probs = dict([(w, uniform_prob) for w in word_probs])

            # save word_probs
            transmissions[word] = word_probs

        self.transmissions = transmissions

    def iterateEM(self, count):
        '''
        Iterate through all transmissions of english to
        foreign words. keep count of repeated occurences
         do until convergence
           set count(e|f) to 0 for all e,f
           set total(f) to 0 for all f
           for all sentence pairs (e_s,f_s)
             set total_s(e) = 0 for all e
             for all words e in e_s
               for all words f in f_s
                 total_s(e) += t(e|f)
             for all words e in e_s
               for all words f in f_s
                 count(e|f) += t(e|f) / total_s(e)
                 total(f)   += t(e|f) / total_s(e)
           for all f
             for all e
               t(e|f) = count(e|f) / total(f)
        '''

        for iter in range(count):

            countef = {}
            totalf = {}
            # set the count of the words to zero
            for word in self.en_words:
                if(word not in self.probs):
                    continue

                word_probs = self.probs[word]

                count = dict([(w, 0) for w in word_probs])
                countef[word] = count
                totalf[word] = 0

            self.countef = countef

            self.totalf = totalf

            # NOW iterate over each word pair
            for (es, ds) in self.sent_pairs:
                es_split = es.split()
                ds_split = ds.split()

                for d in ds_split:
                    self.totals[d] = 0
                    for e in es_split:

                        if (e not in self.transmissions):
                            continue

                        e_trans = self.transmissions[e]

                        if (d not in e_trans):
                            continue

                        self.totals[d] += e_trans[d]

                    # Get count(e|f) and total(f)
                    for e in es_split:
                        if(e not in self.transmissions):
                            continue
                        if (d not in self.transmissions[e]):
                            continue
                        self.countef[e][
                            d] += self.transmissions[e][d] / self.totals[d]
                        self.totalf[
                            e] += self.transmissions[e][d] / self.totals[d]

            for e in self.en_words:
                if (e not in self.probs):
                    continue
                e_prob = self.probs[e]
                for d in e_prob:
                    self.transmissions[e][d] = self.countef[
                        e][d] / self.totalf[e]

class NoMatchError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)


"""
    service_factory.providers.basehttp
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    This module define service provider based on the BaseHTTPHandler.

    :copyright: (c) 2015-2016 by Artem Malyshev.
    :license: GPL3, see LICENSE for more details.
"""

from __future__ import (
    absolute_import, unicode_literals, division, print_function,
)

import socket
import sys
from traceback import print_exc

from service_factory.compat import BaseHTTPRequestHandler, HTTPServer
from service_factory.errors import parse_error
from service_factory.exceptions import ServiceException

class HTTPRequestHandler(BaseHTTPRequestHandler):

    protocol_version = 'HTTP/1.1'
    error_message_format = ''

    def log_request(self, *args):
        """Ignore non error logging messages."""

        pass

    def do_POST(self):
        try:
            content_len = self.headers.get('content-length')
            if content_len and content_len.isdigit():
                raw_data = self.rfile.read(int(content_len))
                data = raw_data.decode('utf-8')
                status, response = self.server.service(data)
            else:
                parse_error()
        except ServiceException as error:
            self.log_error('=' * 80)
            print_exc()
            status, response = error.args

        response = response.encode('utf-8')
        self.send_response(status)
        self.send_header("Content-Length", len(response))
        self.end_headers()
        self.wfile.write(response)

class HTTPServiceProvider(HTTPServer):
    """Base HTTP service provider."""

    def __init__(self, service, host, port,
                 report_message='service factory port {port}'):

        self.service = service
        self.host = host
        self.port = port
        self.report_message = report_message
        self.bind()
        self.report()

    def bind(self):
        """Bind and activate HTTP server."""

        HTTPServer.__init__(self, (self.host, self.port), HTTPRequestHandler)
        self.port = self.server_port

    def report(self):
        """Report startup info to stdout."""

        print(
            self.report_message.format(
                service=self.service,
                host=self.host,
                port=self.port,
            )
        )
        sys.stdout.flush()


from __future__ import absolute_import
import sys
import subprocess
import threading

import six

queue = six.moves.queue

def enqueue_lines(stream, queue):
	for line in iter(stream.readline, b''):
		queue.put(line)
	stream.close()

def Popen_nonblocking(*args, **kwargs):
	"""
	Open a subprocess without blocking. Return a process handle with any
	output streams replaced by queues of lines from that stream.

	Usage::

		proc = Popen_nonblocking(..., stdout=subprocess.PIPE)
		try:
			out_line = proc.stdout.get_nowait()
		except queue.Empty:
			"no output available"
		else:
			handle_output(out_line)
	"""
	kwargs.setdefault('close_fds', 'posix' in sys.builtin_module_names)
	kwargs.setdefault('bufsize', 1)
	proc = subprocess.Popen(*args, **kwargs)
	if proc.stdout:
		q = queue.Queue()
		t = threading.Thread(
			target=enqueue_lines,
			args=(proc.stdout, q))
		proc.stdout = q
		# thread dies with the parent
		t.daemon = True
		t.start()
	if proc.stderr:
		q = queue.Queue()
		t = threading.Thread(
			target=enqueue_lines,
			args=(proc.stderr, q))
		proc.stderr = q
		t.daemon = True
		t.start()
	return proc


"""
pricing.metaconfigure
~~~~~~~~~~~~~~~~~~~

Custom ZCML directive processing.

:copyright: (c) 2018 by Joseph Black.
:license: MIT, see LICENSE for more details.
"""

from zope.component import provideUtility
from pricing.formats import CurrencyFormat
from pricing.interfaces import ICurrencyFormat, IExchange

def _register_currency(name, code, *args):
    currency = CurrencyFormat(name, code, *args)
    provideUtility(currency, ICurrencyFormat, name=code)

def _register_exchange(name, component, backend, base):
    backend = backend(base)
    exchange = component(backend)
    provideUtility(exchange, IExchange, name=name)

class CurrencyHandler:
    """Custom XML Complex-directive Handler for Currency."""

    def __init__(self, _context, default='USD'):
        self.default = default

    def __call__(self):
        pass

    @staticmethod
    def currencyFormat(_context, code, symbol, format,
                       currency_digits=True, decimal_quantization=True,
                       name=''):
        """Handle currencyFormat subdirectives."""
        _context.action(
            discriminator=('currency', name, code),
            callable=_register_currency,
            args=(name, code, symbol, format, currency_digits,
                  decimal_quantization)
            )

    @staticmethod
    def exchange(_context, component, backend, base, name=''):
        """Handle exchange subdirectives."""
        _context.action(
            discriminator=('currency', 'exchange', component),
            callable=_register_exchange,
            args=(name, component, backend, base)
        )


from __future__ import absolute_import, unicode_literals

import threading
import functools

def atomize(f, lock=None):
	"""
	Decorate a function with a reentrant lock to prevent multiple
	threads from calling said thread simultaneously.
	"""
	lock = lock or threading.RLock()

	@functools.wraps(f)
	def exec_atomic(*args, **kwargs):
		lock.acquire()
		try:
			return f(*args, **kwargs)
		finally:
			lock.release()
	return exec_atomic

class AtomicGuard(object):
	"""
	A decorator that can be applied to multiple functions/methods to
	prevent more than one of them from being entered at any one time.
	"""
	def __init__(self):
		self.lock = threading.RLock()

	def __call__(self, f):
		return atomize(f, lock=self.lock)


"""
    service_factory.factory
    ~~~~~~~~~~~~~~~~~~~~~~~

    This module define service factory.

    :copyright: (c) 2015-2016 by Artem Malyshev.
    :license: GPL3, see LICENSE for more details.
"""

from __future__ import (
    absolute_import, unicode_literals, division, print_function,
)

from .providers.basehttp import HTTPServiceProvider
from .service import Service

def service_factory(app, host, port,
                    report_message='service factory port {port}',
                    provider_cls=HTTPServiceProvider):
    """Create service, start server.

    :param app: application to instantiate a service
    :param host: interface to bound provider
    :param port: port to bound provider
    :param report_message: message format to report port
    :param provider_cls: server class provide a service

    """

    service = Service(app)
    server = provider_cls(service, host, port, report_message)
    server.serve_forever()


"""
    service_factory.errors
    ~~~~~~~~~~~~~~~~~~~~~~

    This module implements different errors emitters.

    :copyright: (c) 2015-2016 by Artem Malyshev.
    :license: GPL3, see LICENSE for more details.
"""

from __future__ import (
    absolute_import, unicode_literals, division, print_function,
)
from json import dumps

from .exceptions import ServiceException

def parse_error():
    """JSON-RPC parse error."""

    response = {
        'jsonrpc': '2.0',
        'id': None,
        'error': {
            'code': -32700,
            'message': 'Parse error',
        },
    }
    raise ServiceException(400, dumps(response))

def invalid_request(error):
    """JSON-RPC invalid request error.

    :param error: request error
    :type error: Exception

    """

    response = {
        'jsonrpc': '2.0',
        'id': None,
        'error': {
            'code': -32600,
            'message': 'Invalid Request',
            'data': repr(error),
        },
    }
    raise ServiceException(400, dumps(response))

def method_not_found(request_id):
    """JSON-RPC method not found error.

    :param request_id: JSON-RPC request id
    :type request_id: int or str or None

    """

    response = {
        'jsonrpc': '2.0',
        'id': request_id,
        'error': {
            'code': -32601,
            'message': 'Method not found',
        },
    }
    raise ServiceException(400, dumps(response))

def server_error(request_id, error):
    """JSON-RPC server error.

    :param request_id: JSON-RPC request id
    :type request_id: int or str or None
    :param error: server error
    :type error: Exception

    """

    response = {
        'jsonrpc': '2.0',
        'id': request_id,
        'error': {
            'code': -32000,
            'message': 'Server error',
            'data': repr(error),
        },
    }
    raise ServiceException(500, dumps(response))