from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def round(self, ndigits: int = 0) -> "Price":
        """
        Rounds the quantity of the monetary value to ``ndigits`` by using ``HALF_EVEN`` method if *defined*, itself
        otherwise.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def add(self, other: "Price") -> "Price":
        """
        Performs monetary addition on the price object and the given ``other`` price object.

        Note that::

        1. Raises :class:`IncompatibleCurrencyError` if currencies do not match.
        2. If any of the operands are undefined, returns the other one conveniently.
        3. Dates are carried forward as a result of addition of two defined price objects.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def scalar_add(self, other: Numeric) -> "Price":
        """
        Performs scalar addition on the quantity of the price.

        Note that undefined price object is returned as is.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def subtract(self, other: "Price") -> "Price":
        """
        Performs monetary subtraction on the price object and the given ``other`` price object.

        Note that::

        1. Raises :class:`IncompatibleCurrencyError` if currencies do not match.
        2. If any of the operands are undefined, returns the other one conveniently.
        3. Dates are carried forward as a result of addition of two defined price objects.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def scalar_subtract(self, other: Numeric) -> "Price":
        """
        Performs scalar subtraction on the quantity of the price.

        Note that undefined price object is returned as is.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def multiply(self, other: Numeric) -> "Price":
        """
        Performs scalar multiplication.

        Note that undefined price object is returned as is.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def times(self, other: Numeric) -> "Money":
        """
        Performs monetary multiplication operation.

        Note that undefined price object is returned as is.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def divide(self, other: Numeric) -> "Price":
        """
        Performs ordinary division on the price object if *defined*, itself otherwise.

        Note that division by zero yields an undefined price object.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def floor_divide(self, other: Numeric) -> "Price":
        """
        Performs floor division on the price object if *defined*, itself otherwise.

        Note that division by zero yields an undefined price object.

        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def lt(self, other: "Price") -> bool:
        """
        Applies "less than" comparison against ``other`` price.

        Note that::

        1. Undefined price objects are always less than ``other`` if ``other`` is not undefined, and
        2. :class:`IncompatibleCurrencyError` is raised when comparing two defined price objects with different
        currencies.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def lte(self, other: "Price") -> bool:
        """
        Applies "less than or equal to" comparison against ``other`` price.

        Note that::

        1. Undefined price objects are always less than or equal to ``other``, and
        2. :class:`IncompatibleCurrencyError` is raised when comparing two defined price objects with different
        currencies.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def gt(self, other: "Price") -> bool:
        """
        Applies "greater than" comparison against ``other`` price.

        Note that::

        1. Undefined price objects are never greater than ``other``,
        2. Defined price objects are always greater than ``other`` if other is undefined, and
        3. :class:`IncompatibleCurrencyError` is raised when comparing two defined price objects with different
        currencies.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class IncompatibleCurrencyError(ValueError):

    def __init__(self, ccy1: Currency, ccy2: Currency, operation: str = "<Unspecified>") -> None:
        """
        Initializes an incompatible currency error message.
        """
        ## Keep sloys:
        self.ccy1 = ccy1
        self.ccy2 = ccy2
        self.operation = operation

        ## Call super:
        super().__init__(f"{ccy1.code} vs {ccy2.code} are incompatible for operation '{operation}'.")

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def gte(self, other: "Price") -> bool:
        """
        Applies "greater than or equal to" comparison against ``other`` price.

        Note that::

        1. Undefined price objects are never greater than or equal to ``other`` if ``other`` is defined,
        2. Undefined price objects are greater than or equal to ``other`` if ``other is undefined, and
        3. :class:`IncompatibleCurrencyError` is raised when comparing two defined price objects with different
        currencies.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def with_ccy(self, ccy: Currency) -> "Price":
        """
        Creates a new price object with the given currency if price is *defined*, returns itself otherwise.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def with_qty(self, qty: Decimal) -> "Price":
        """
        Creates a new price object with the given quantity if price is *defined*, returns itself otherwise.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def with_dov(self, dov: Date) -> "Price":
        """
        Creates a new price object with the given value date if price is *defined*, returns itself otherwise.
        """
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def convert(self, to: Currency, asof: Optional[Date] = None, strict: bool = False) -> "Price":
        """
        Converts the monetary value from one currency to another.

        Raises :class:`FXRateLookupError` if no foreign exchange rate can be found for conversion.

        Note that we will carry the date forward as per ``asof`` date.
        """
        raise NotImplementedError


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __bool__(self) -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __eq__(self, other: Any) -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __abs__(self) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __float__(self) -> float:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __int__(self) -> int:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __neg__(self) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __pos__(self) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __add__(self, other: "Price") -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __sub__(self, other: "Price") -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __mul__(self, other: Numeric) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __truediv__(self, other: Numeric) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __floordiv__(self, other: Numeric) -> "Price":
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __lt__(self, other: "Price") -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __le__(self, other: "Price") -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __gt__(self, other: "Price") -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class Price:

    __slots__ = ()

    NA: "Price"

    ccy: Currency

    qty: Decimal

    dov: Date

    defined: bool

    undefined: bool

    @abstractmethod
    def __ge__(self, other: "Price") -> bool:
        pass


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class SomePrice(Price, NamedTuple("SomePrice", [("ccy", Currency), ("qty", Decimal), ("dov", Date)])):

    __slots__ = ()

    defined = True

    undefined = False

    __bool__ = as_boolean

    __eq__ = is_equal

    __abs__ = abs

    __float__ = as_float

    __int__ = as_integer

    __neg__ = negative

    __pos__ = positive

    __add__ = add

    __sub__ = subtract

    __mul__ = multiply

    __truediv__ = divide

    __floordiv__ = floor_divide

    __lt__ = lt

    __le__ = lte

    __gt__ = gt

    __ge__ = gte

    def with_dov(self, dov: Date) -> "Price":
        return SomePrice(self[0], self[1], dov)


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class SomePrice(Price, NamedTuple("SomePrice", [("ccy", Currency), ("qty", Decimal), ("dov", Date)])):

    __slots__ = ()

    defined = True

    undefined = False

    __bool__ = as_boolean

    __eq__ = is_equal

    __abs__ = abs

    __float__ = as_float

    __int__ = as_integer

    __neg__ = negative

    __pos__ = positive

    __add__ = add

    __sub__ = subtract

    __mul__ = multiply

    __truediv__ = divide

    __floordiv__ = floor_divide

    __lt__ = lt

    __le__ = lte

    __gt__ = gt

    __ge__ = gte

    def convert(self, to: Currency, asof: Optional[Date] = None, strict: bool = False) -> "Price":
        ## Get slots:
        ccy, qty, dov = self

        ## Get date of conversion:
        asof = asof or dov

        ## Attempt to get the FX rate:
        try:
            rate = FXRateService.default.query(ccy, to, asof, strict)  # type: ignore
        except AttributeError as exc:
            if FXRateService.default is None:
                raise ProgrammingError("Did you implement and set the default FX rate service?")
            else:
                raise exc

        ## Do we have a rate?
        if rate is None:
            ## Nope, shall we raise exception?
            if strict:
                ## Yep:
                raise FXRateLookupError(ccy, to, asof)
            else:
                ## Just return NA:
                return NoPrice

        ## Compute and return:
        return SomePrice(to, qty * rate.value, asof)


from abc import abstractmethod
from decimal import Decimal, DivisionByZero, InvalidOperation
from typing import Any, NamedTuple, Optional, Union, overload
from .commons.errors import ProgrammingError
from .commons.numbers import Numeric
from .commons.zeitgeist import Date
from .currencies import Currency
from .exchange import FXRateLookupError, FXRateService

__all__ = [
    "IncompatibleCurrencyError",
    "MonetaryOperationException",
    "Money",
    "NoMoney",
    "NoPrice",
    "NoneMoney",
    "NonePrice",
    "Price",
    "SomeMoney",
    "SomePrice",
]
Money.NA = NoMoney = NoneMoney()
Price.NA = NoPrice = NonePrice()
Price.NA = NoPrice = NonePrice()

class NonePrice(Price):

    __slots__ = ()

    defined = False

    undefined = True

    money = NoMoney

    __bool__ = as_boolean

    __eq__ = is_equal

    __abs__ = abs

    __float__ = as_float

    __int__ = as_integer

    __neg__ = negative

    __pos__ = positive

    __add__ = add

    __sub__ = subtract

    __mul__ = multiply

    __truediv__ = divide

    __floordiv__ = floor_divide

    __lt__ = lt

    __le__ = lte

    __gt__ = gt

    __ge__ = gte

    def with_dov(self, dov: Date) -> "Price":
        return self


import abc
import os
import inspect
import sys
import datetime as datetime_module

PY3 = (sys.version_info[0] == 3)
PY2 = not PY3

def timedelta_format(timedelta):
    time = (datetime_module.datetime.min + timedelta).time()
    return time_isoformat(time, timespec='microseconds')


import abc
import os
import inspect
import sys
import datetime as datetime_module

PY3 = (sys.version_info[0] == 3)
PY2 = not PY3

def timedelta_parse(s):
    hours, minutes, seconds, microseconds = map(
        int,
        s.replace('.', ':').split(':')
    )
    return datetime_module.timedelta(hours=hours, minutes=minutes,
                                     seconds=seconds,
                                     microseconds=microseconds)


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

def get_local_reprs(frame, watch=(), custom_repr=(), max_length=None, normalize=False):
    code = frame.f_code
    vars_order = (code.co_varnames + code.co_cellvars + code.co_freevars +
                  tuple(frame.f_locals.keys()))

    result_items = [(key, utils.get_shortish_repr(value, custom_repr,
                                                  max_length, normalize))
                    for key, value in frame.f_locals.items()]
    result_items.sort(key=lambda key_value: vars_order.index(key_value[0]))
    result = collections.OrderedDict(result_items)

    for variable in watch:
        result.update(sorted(variable.items(frame, normalize)))
    return result


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

def get_path_and_source_from_frame(frame):
    globs = frame.f_globals or {}
    module_name = globs.get('__name__')
    file_name = frame.f_code.co_filename
    cache_key = (module_name, file_name)
    try:
        return source_and_path_cache[cache_key]
    except KeyError:
        pass
    loader = globs.get('__loader__')

    source = None
    if hasattr(loader, 'get_source'):
        try:
            source = loader.get_source(module_name)
        except ImportError:
            pass
        if source is not None:
            source = source.splitlines()
    if source is None:
        ipython_filename_match = ipython_filename_pattern.match(file_name)
        if ipython_filename_match:
            entry_number = int(ipython_filename_match.group(1))
            try:
                import IPython
                ipython_shell = IPython.get_ipython()
                ((_, _, source_chunk),) = ipython_shell.history_manager. \
                                  get_range(0, entry_number, entry_number + 1)
                source = source_chunk.splitlines()
            except Exception:
                pass
        else:
            try:
                with open(file_name, 'rb') as fp:
                    source = fp.read().splitlines()
            except utils.file_reading_errors:
                pass
    if not source:
        # We used to check `if source is None` but I found a rare bug where it
        # was empty, but not `None`, so now we check `if not source`.
        source = UnavailableSource()

    # If we just read the source from a file, or if the loader did not
    # apply tokenize.detect_encoding to decode the source into a
    # string, then we should do that ourselves.
    if isinstance(source[0], bytes):
        encoding = 'utf-8'
        for line in source[:2]:
            # File coding may be specified. Match pattern from PEP-263
            # (https://www.python.org/dev/peps/pep-0263/)
            match = re.search(br'coding[:=]\s*([-\w.]+)', line)
            if match:
                encoding = match.group(1).decode('ascii')
                break
        source = [pycompat.text_type(sline, encoding, 'replace') for sline in
                  source]

    result = (file_name, source)
    source_and_path_cache[cache_key] = result
    return result


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class FileWriter(object):

    def __init__(self, path, overwrite):
        self.path = pycompat.text_type(path)
        self.overwrite = overwrite

def get_write_function(output, overwrite):
    is_path = isinstance(output, (pycompat.PathLike, str))
    if overwrite and not is_path:
        raise Exception('`overwrite=True` can only be used when writing '
                        'content to file.')
    if output is None:
        def write(s):
            stderr = sys.stderr
            try:
                stderr.write(s)
            except UnicodeEncodeError:
                # God damn Python 2
                stderr.write(utils.shitcode(s))
    elif is_path:
        return FileWriter(output, overwrite).write
    elif callable(output):
        write = output
    else:
        assert isinstance(output, utils.WritableStream)

        def write(s):
            output.write(s)
    return write


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class FileWriter(object):

    def __init__(self, path, overwrite):
        self.path = pycompat.text_type(path)
        self.overwrite = overwrite

    def write(self, s):
        with open(self.path, 'w' if self.overwrite else 'a',
                  encoding='utf-8') as output_file:
            output_file.write(s)
        self.overwrite = False


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class Tracer:

    def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
                 prefix='', overwrite=False, thread_info=False, custom_repr=(),
                 max_variable_length=100, normalize=False, relative_time=False):
        self._write = get_write_function(output, overwrite)

        self.watch = [
            v if isinstance(v, BaseVariable) else CommonVariable(v)
            for v in utils.ensure_tuple(watch)
         ] + [
             v if isinstance(v, BaseVariable) else Exploding(v)
             for v in utils.ensure_tuple(watch_explode)
        ]
        self.frame_to_local_reprs = {}
        self.start_times = {}
        self.depth = depth
        self.prefix = prefix
        self.thread_info = thread_info
        self.thread_info_padding = 0
        assert self.depth >= 1
        self.target_codes = set()
        self.target_frames = set()
        self.thread_local = threading.local()
        if len(custom_repr) == 2 and not all(isinstance(x,
                      pycompat.collections_abc.Iterable) for x in custom_repr):
            custom_repr = (custom_repr,)
        self.custom_repr = custom_repr
        self.last_source_path = None
        self.max_variable_length = max_variable_length
        self.normalize = normalize
        self.relative_time = relative_time


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class Tracer:

    def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
                 prefix='', overwrite=False, thread_info=False, custom_repr=(),
                 max_variable_length=100, normalize=False, relative_time=False):
        self._write = get_write_function(output, overwrite)

        self.watch = [
            v if isinstance(v, BaseVariable) else CommonVariable(v)
            for v in utils.ensure_tuple(watch)
         ] + [
             v if isinstance(v, BaseVariable) else Exploding(v)
             for v in utils.ensure_tuple(watch_explode)
        ]
        self.frame_to_local_reprs = {}
        self.start_times = {}
        self.depth = depth
        self.prefix = prefix
        self.thread_info = thread_info
        self.thread_info_padding = 0
        assert self.depth >= 1
        self.target_codes = set()
        self.target_frames = set()
        self.thread_local = threading.local()
        if len(custom_repr) == 2 and not all(isinstance(x,
                      pycompat.collections_abc.Iterable) for x in custom_repr):
            custom_repr = (custom_repr,)
        self.custom_repr = custom_repr
        self.last_source_path = None
        self.max_variable_length = max_variable_length
        self.normalize = normalize
        self.relative_time = relative_time

    def __call__(self, function_or_class):
        if DISABLED:
            return function_or_class

        if inspect.isclass(function_or_class):
            return self._wrap_class(function_or_class)
        else:
            return self._wrap_function(function_or_class)


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class Tracer:

    def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
                 prefix='', overwrite=False, thread_info=False, custom_repr=(),
                 max_variable_length=100, normalize=False, relative_time=False):
        self._write = get_write_function(output, overwrite)

        self.watch = [
            v if isinstance(v, BaseVariable) else CommonVariable(v)
            for v in utils.ensure_tuple(watch)
         ] + [
             v if isinstance(v, BaseVariable) else Exploding(v)
             for v in utils.ensure_tuple(watch_explode)
        ]
        self.frame_to_local_reprs = {}
        self.start_times = {}
        self.depth = depth
        self.prefix = prefix
        self.thread_info = thread_info
        self.thread_info_padding = 0
        assert self.depth >= 1
        self.target_codes = set()
        self.target_frames = set()
        self.thread_local = threading.local()
        if len(custom_repr) == 2 and not all(isinstance(x,
                      pycompat.collections_abc.Iterable) for x in custom_repr):
            custom_repr = (custom_repr,)
        self.custom_repr = custom_repr
        self.last_source_path = None
        self.max_variable_length = max_variable_length
        self.normalize = normalize
        self.relative_time = relative_time

    def __enter__(self):
        if DISABLED:
            return
        thread_global.__dict__.setdefault('depth', -1)
        calling_frame = inspect.currentframe().f_back
        if not self._is_internal_frame(calling_frame):
            calling_frame.f_trace = self.trace
            self.target_frames.add(calling_frame)

        stack = self.thread_local.__dict__.setdefault(
            'original_trace_functions', []
        )
        stack.append(sys.gettrace())
        self.start_times[calling_frame] = datetime_module.datetime.now()
        sys.settrace(self.trace)


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class Tracer:

    def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
                 prefix='', overwrite=False, thread_info=False, custom_repr=(),
                 max_variable_length=100, normalize=False, relative_time=False):
        self._write = get_write_function(output, overwrite)

        self.watch = [
            v if isinstance(v, BaseVariable) else CommonVariable(v)
            for v in utils.ensure_tuple(watch)
         ] + [
             v if isinstance(v, BaseVariable) else Exploding(v)
             for v in utils.ensure_tuple(watch_explode)
        ]
        self.frame_to_local_reprs = {}
        self.start_times = {}
        self.depth = depth
        self.prefix = prefix
        self.thread_info = thread_info
        self.thread_info_padding = 0
        assert self.depth >= 1
        self.target_codes = set()
        self.target_frames = set()
        self.thread_local = threading.local()
        if len(custom_repr) == 2 and not all(isinstance(x,
                      pycompat.collections_abc.Iterable) for x in custom_repr):
            custom_repr = (custom_repr,)
        self.custom_repr = custom_repr
        self.last_source_path = None
        self.max_variable_length = max_variable_length
        self.normalize = normalize
        self.relative_time = relative_time

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if DISABLED:
            return
        stack = self.thread_local.original_trace_functions
        sys.settrace(stack.pop())
        calling_frame = inspect.currentframe().f_back
        self.target_frames.discard(calling_frame)
        self.frame_to_local_reprs.pop(calling_frame, None)

        ### Writing elapsed time: #############################################
        #                                                                     #
        start_time = self.start_times.pop(calling_frame)
        duration = datetime_module.datetime.now() - start_time
        elapsed_time_string = pycompat.timedelta_format(duration)
        indent = ' ' * 4 * (thread_global.depth + 1)
        self.write(
            '{indent}Elapsed time: {elapsed_time_string}'.format(**locals())
        )
        #                                                                     #
        ### Finished writing elapsed time. ####################################


import functools
import inspect
import opcode
import os
import sys
import re
import collections
import datetime as datetime_module
import itertools
import threading
import traceback
from .variables import CommonVariable, Exploding, BaseVariable
from . import utils, pycompat

ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
source_and_path_cache = {}
thread_global = threading.local()
DISABLED = bool(os.getenv('PYSNOOPER_DISABLED', ''))

class Tracer:

    def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
                 prefix='', overwrite=False, thread_info=False, custom_repr=(),
                 max_variable_length=100, normalize=False, relative_time=False):
        self._write = get_write_function(output, overwrite)

        self.watch = [
            v if isinstance(v, BaseVariable) else CommonVariable(v)
            for v in utils.ensure_tuple(watch)
         ] + [
             v if isinstance(v, BaseVariable) else Exploding(v)
             for v in utils.ensure_tuple(watch_explode)
        ]
        self.frame_to_local_reprs = {}
        self.start_times = {}
        self.depth = depth
        self.prefix = prefix
        self.thread_info = thread_info
        self.thread_info_padding = 0
        assert self.depth >= 1
        self.target_codes = set()
        self.target_frames = set()
        self.thread_local = threading.local()
        if len(custom_repr) == 2 and not all(isinstance(x,
                      pycompat.collections_abc.Iterable) for x in custom_repr):
            custom_repr = (custom_repr,)
        self.custom_repr = custom_repr
        self.last_source_path = None
        self.max_variable_length = max_variable_length
        self.normalize = normalize
        self.relative_time = relative_time

    def trace(self, frame, event, arg):

        ### Checking whether we should trace this line: #######################
        #                                                                     #
        # We should trace this line either if it's in the decorated function,
        # or the user asked to go a few levels deeper and we're within that
        # number of levels deeper.

        if not (frame.f_code in self.target_codes or frame in self.target_frames):
            if self.depth == 1:
                # We did the most common and quickest check above, because the
                # trace function runs so incredibly often, therefore it's
                # crucial to hyper-optimize it for the common case.
                return None
            elif self._is_internal_frame(frame):
                return None
            else:
                _frame_candidate = frame
                for i in range(1, self.depth):
                    _frame_candidate = _frame_candidate.f_back
                    if _frame_candidate is None:
                        return None
                    elif _frame_candidate.f_code in self.target_codes or _frame_candidate in self.target_frames:
                        break
                else:
                    return None

        if event == 'call':
            thread_global.depth += 1
        indent = ' ' * 4 * thread_global.depth

        #                                                                     #
        ### Finished checking whether we should trace this line. ##############

        ### Making timestamp: #################################################
        #                                                                     #
        if self.normalize:
            timestamp = ' ' * 15
        elif self.relative_time:
            try:
                start_time = self.start_times[frame]
            except KeyError:
                start_time = self.start_times[frame] = \
                                                 datetime_module.datetime.now()
            duration = datetime_module.datetime.now() - start_time
            timestamp = pycompat.timedelta_format(duration)
        else:
            timestamp = pycompat.time_isoformat(
                datetime_module.datetime.now().time(),
                timespec='microseconds'
            )
        #                                                                     #
        ### Finished making timestamp. ########################################

        line_no = frame.f_lineno
        source_path, source = get_path_and_source_from_frame(frame)
        source_path = source_path if not self.normalize else os.path.basename(source_path)
        if self.last_source_path != source_path:
            self.write(u'{indent}Source path:... {source_path}'.
                       format(**locals()))
            self.last_source_path = source_path
        source_line = source[line_no - 1]
        thread_info = ""
        if self.thread_info:
            if self.normalize:
                raise NotImplementedError("normalize is not supported with "
                                          "thread_info")
            current_thread = threading.current_thread()
            thread_info = "{ident}-{name} ".format(
                ident=current_thread.ident, name=current_thread.getName())
        thread_info = self.set_thread_info_padding(thread_info)

        ### Reporting newish and modified variables: ##########################
        #                                                                     #
        old_local_reprs = self.frame_to_local_reprs.get(frame, {})
        self.frame_to_local_reprs[frame] = local_reprs = \
                                       get_local_reprs(frame,
                                                       watch=self.watch, custom_repr=self.custom_repr,
                                                       max_length=self.max_variable_length,
                                                       normalize=self.normalize,
                                                       )

        newish_string = ('Starting var:.. ' if event == 'call' else
                                                            'New var:....... ')

        for name, value_repr in local_reprs.items():
            if name not in old_local_reprs:
                self.write('{indent}{newish_string}{name} = {value_repr}'.format(
                                                                       **locals()))
            elif old_local_reprs[name] != value_repr:
                self.write('{indent}Modified var:.. {name} = {value_repr}'.format(
                                                                   **locals()))

        #                                                                     #
        ### Finished newish and modified variables. ###########################

        ### Dealing with misplaced function definition: #######################
        #                                                                     #
        if event == 'call' and source_line.lstrip().startswith('@'):
            # If a function decorator is found, skip lines until an actual
            # function definition is found.
            for candidate_line_no in itertools.count(line_no):
                try:
                    candidate_source_line = source[candidate_line_no - 1]
                except IndexError:
                    # End of source file reached without finding a function
                    # definition. Fall back to original source line.
                    break

                if candidate_source_line.lstrip().startswith('def'):
                    # Found the def line!
                    line_no = candidate_line_no
                    source_line = candidate_source_line
                    break
        #                                                                     #
        ### Finished dealing with misplaced function definition. ##############

        # If a call ends due to an exception, we still get a 'return' event
        # with arg = None. This seems to be the only way to tell the difference
        # https://stackoverflow.com/a/12800909/2482744
        code_byte = frame.f_code.co_code[frame.f_lasti]
        if not isinstance(code_byte, int):
            code_byte = ord(code_byte)
        ended_by_exception = (
                event == 'return'
                and arg is None
                and (opcode.opname[code_byte]
                     not in ('RETURN_VALUE', 'YIELD_VALUE'))
        )

        if ended_by_exception:
            self.write('{indent}Call ended by exception'.
                       format(**locals()))
        else:
            self.write(u'{indent}{timestamp} {thread_info}{event:9} '
                       u'{line_no:4} {source_line}'.format(**locals()))

        if event == 'return':
            self.frame_to_local_reprs.pop(frame, None)
            self.start_times.pop(frame, None)
            thread_global.depth -= 1

            if not ended_by_exception:
                return_value_repr = utils.get_shortish_repr(arg,
                                                            custom_repr=self.custom_repr,
                                                            max_length=self.max_variable_length,
                                                            normalize=self.normalize,
                                                            )
                self.write('{indent}Return value:.. {return_value_repr}'.
                           format(**locals()))

        if event == 'exception':
            exception = '\n'.join(traceback.format_exception_only(*arg[:2])).strip()
            if self.max_variable_length:
                exception = utils.truncate(exception, self.max_variable_length)
            self.write('{indent}Exception:..... {exception}'.
                       format(**locals()))

        return self.trace


import abc
import re
import sys
from .pycompat import ABC, string_types, collections_abc

file_reading_errors = (
    IOError,
    OSError,
    ValueError # IronPython weirdness.
)
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')

def shitcode(s):
    return ''.join(
        (c if (0 < ord(c) < 256) else '?') for c in s
    )


import abc
import re
import sys
from .pycompat import ABC, string_types, collections_abc

file_reading_errors = (
    IOError,
    OSError,
    ValueError # IronPython weirdness.
)
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')

def get_repr_function(item, custom_repr):
    for condition, action in custom_repr:
        if isinstance(condition, type):
            condition = lambda x, y=condition: isinstance(x, y)
        if condition(item):
            return action
    return repr


import abc
import re
import sys
from .pycompat import ABC, string_types, collections_abc

file_reading_errors = (
    IOError,
    OSError,
    ValueError # IronPython weirdness.
)
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')

def get_shortish_repr(item, custom_repr=(), max_length=None, normalize=False):
    repr_function = get_repr_function(item, custom_repr)
    try:
        r = repr_function(item)
    except Exception:
        r = 'REPR FAILED'
    r = r.replace('\r', '').replace('\n', '')
    if normalize:
        r = normalize_repr(r)
    if max_length:
        r = truncate(r, max_length)
    return r


import abc
import re
import sys
from .pycompat import ABC, string_types, collections_abc

file_reading_errors = (
    IOError,
    OSError,
    ValueError # IronPython weirdness.
)
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')

def truncate(string, max_length):
    if (max_length is None) or (len(string) <= max_length):
        return string
    else:
        left = (max_length - 3) // 2
        right = max_length - 3 - left
        return u'{}...{}'.format(string[:left], string[-right:])


import abc
import re
import sys
from .pycompat import ABC, string_types, collections_abc

file_reading_errors = (
    IOError,
    OSError,
    ValueError # IronPython weirdness.
)
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')

class WritableStream(ABC):

    @abc.abstractmethod
    def write(self, s):
        pass


import itertools
import abc
from copy import deepcopy
from . import utils
from . import pycompat

class BaseVariable(pycompat.ABC):

    def __init__(self, source, exclude=()):
        self.source = source
        self.exclude = utils.ensure_tuple(exclude)
        self.code = compile(source, '<variable>', 'eval')
        if needs_parentheses(source):
            self.unambiguous_source = '({})'.format(source)
        else:
            self.unambiguous_source = source


import itertools
import abc
from copy import deepcopy
from . import utils
from . import pycompat

class BaseVariable(pycompat.ABC):

    def __init__(self, source, exclude=()):
        self.source = source
        self.exclude = utils.ensure_tuple(exclude)
        self.code = compile(source, '<variable>', 'eval')
        if needs_parentheses(source):
            self.unambiguous_source = '({})'.format(source)
        else:
            self.unambiguous_source = source

    def items(self, frame, normalize=False):
        try:
            main_value = eval(self.code, frame.f_globals or {}, frame.f_locals)
        except Exception:
            return ()
        return self._items(main_value, normalize)


import itertools
import abc
from copy import deepcopy
from . import utils
from . import pycompat

class BaseVariable(pycompat.ABC):

    def __init__(self, source, exclude=()):
        self.source = source
        self.exclude = utils.ensure_tuple(exclude)
        self.code = compile(source, '<variable>', 'eval')
        if needs_parentheses(source):
            self.unambiguous_source = '({})'.format(source)
        else:
            self.unambiguous_source = source

    def __eq__(self, other):
        return (isinstance(other, BaseVariable) and
                                       self._fingerprint == other._fingerprint)


import itertools
import abc
from copy import deepcopy
from . import utils
from . import pycompat

class Indices(Keys):

    _slice = slice(None)

    def __getitem__(self, item):
        assert isinstance(item, slice)
        result = deepcopy(self)
        result._slice = item
        return result


import collections
import os
import re
import typing

def parse_env_file_contents(lines: typing.Iterable[str] = None) -> typing.Generator[typing.Tuple[str, str], None, None]:
    """
    Parses env file content.

    From honcho.

    >>> lines = ['TEST=${HOME}/yeee', 'THISIS=~/a/test', 'YOLO=~/swaggins/$NONEXISTENT_VAR_THAT_DOES_NOT_EXIST']
    >>> load_env_file(lines, write_environ=dict())
    OrderedDict([('TEST', '.../yeee'),
             ('THISIS', '.../a/test'),
             ('YOLO',
              '.../swaggins/$NONEXISTENT_VAR_THAT_DOES_NOT_EXIST')])

    """
    for line in lines:
        m1 = re.match(r'\A([A-Za-z_0-9]+)=(.*)\Z', line)

        if m1:
            key, val = m1.group(1), m1.group(2)

            m2 = re.match(r"\A'(.*)'\Z", val)
            if m2:
                val = m2.group(1)

            m3 = re.match(r'\A"(.*)"\Z', val)
            if m3:
                val = re.sub(r'\\(.)', r'\1', m3.group(1))

            yield key, val


import collections
import os
import re
import typing

def load_env_file(lines: typing.Iterable[str], write_environ: typing.MutableMapping = os.environ) -> collections.OrderedDict:
    """
    Loads (and returns) an env file specified by `filename` into the mapping `environ`.

    >>> lines = ['TEST=${HOME}/yeee-$PATH', 'THISIS=~/a/test', 'YOLO=~/swaggins/$NONEXISTENT_VAR_THAT_DOES_NOT_EXIST']
    >>> load_env_file(lines, write_environ=dict())
    OrderedDict([('TEST', '.../.../yeee-...:...'),
             ('THISIS', '.../a/test'),
             ('YOLO',
              '.../swaggins/$NONEXISTENT_VAR_THAT_DOES_NOT_EXIST')])
    """
    values = parse_env_file_contents(lines)

    changes = collections.OrderedDict()

    for k, v in values:
        v = expand(v)

        changes[k] = v

        if write_environ is not None:
            write_environ[k] = v

    return changes


import os
import sys
import functools

islurp.LINEMODE = LINEMODE
islurp.LINEMODE = LINEMODE
slurp = islurp

def islurp(filename, mode='r', iter_by=LINEMODE, allow_stdin=True, expanduser=True, expandvars=True):
    """
    Read [expanded] `filename` and yield each (line | chunk).

    :param str filename: File path
    :param str mode: Use this mode to open `filename`, ala `r` for text (default), `rb` for binary, etc.
    :param int iter_by: Iterate by this many bytes at a time. Default is by line.
    :param bool allow_stdin: If Truthy and filename is `-`, read from `sys.stdin`.
    :param bool expanduser: If Truthy, expand `~` in `filename`
    :param bool expandvars: If Truthy, expand env vars in `filename`
    """
    if iter_by == 'LINEMODE':
        iter_by = LINEMODE

    fh = None
    try:
        if filename == '-' and allow_stdin:
            fh = sys.stdin
        else:
            if expanduser:
                filename = os.path.expanduser(filename)
            if expandvars:
                filename = os.path.expandvars(filename)

            fh = open(filename, mode)
            fh_next = fh.readline if iter_by == LINEMODE else functools.partial(fh.read, iter_by)

        while True:
            buf = fh_next()
            if buf == '':  # EOF
                break
            yield buf
    finally:
        if fh and fh != sys.stdin:
            fh.close()


import os
import sys
import functools

islurp.LINEMODE = LINEMODE
islurp.LINEMODE = LINEMODE
slurp = islurp

def burp(filename, contents, mode='w', allow_stdout=True, expanduser=True, expandvars=True):
    """
    Write `contents` to `filename`.
    """
    if filename == '-' and allow_stdout:
        sys.stdout.write(contents)
    else:
        if expanduser:
            filename = os.path.expanduser(filename)
        if expandvars:
            filename = os.path.expandvars(filename)

        with open(filename, mode) as fh:
            fh.write(contents)


class IllegalUseOfScopeReplacer(Exception):

    _fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
            " %(msg)s%(extra)s")

    def __init__(self, name, msg, extra=None):
        self.name = name
        self.msg = msg
        if extra:
            self.extra = ': ' + str(extra)
        else:
            self.extra = ''

        super(IllegalUseOfScopeReplacer, self).__init__()

    def __unicode__(self):
        u = self._format()
        if isinstance(u, str):
            # Try decoding the str using the default encoding.
            u = unicode(u)
        elif not isinstance(u, unicode):
            # Try to make a unicode object from it, because __unicode__ must
            # return a unicode object.
            u = unicode(u)
        return u


class IllegalUseOfScopeReplacer(Exception):

    _fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
            " %(msg)s%(extra)s")

    def __init__(self, name, msg, extra=None):
        self.name = name
        self.msg = msg
        if extra:
            self.extra = ': ' + str(extra)
        else:
            self.extra = ''

        super(IllegalUseOfScopeReplacer, self).__init__()

    def __str__(self):
        s = self._format()
        if isinstance(s, unicode):
            s = s.encode('utf8')
        else:
            # __str__ must return a str.
            s = str(s)
        return s


class IllegalUseOfScopeReplacer(Exception):

    _fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
            " %(msg)s%(extra)s")

    def __init__(self, name, msg, extra=None):
        self.name = name
        self.msg = msg
        if extra:
            self.extra = ': ' + str(extra)
        else:
            self.extra = ''

        super(IllegalUseOfScopeReplacer, self).__init__()

    def __eq__(self, other):
        if self.__class__ is not other.__class__:
            return NotImplemented
        return self.__dict__ == other.__dict__


class ScopeReplacer(object):

    __slots__ = ('_scope', '_factory', '_name', '_real_obj')

    _should_proxy = True

    def __init__(self, scope, factory, name):
        """Create a temporary object in the specified scope.
        Once used, a real object will be placed in the scope.

        :param scope: The scope the object should appear in
        :param factory: A callable that will create the real object.
            It will be passed (self, scope, name)
        :param name: The variable name in the given scope.
        """
        object.__setattr__(self, '_scope', scope)
        object.__setattr__(self, '_factory', factory)
        object.__setattr__(self, '_name', name)
        object.__setattr__(self, '_real_obj', None)
        scope[name] = self

    def __getattribute__(self, attr):
        obj = object.__getattribute__(self, '_resolve')()
        return getattr(obj, attr)


class ScopeReplacer(object):

    __slots__ = ('_scope', '_factory', '_name', '_real_obj')

    _should_proxy = True

    def __init__(self, scope, factory, name):
        """Create a temporary object in the specified scope.
        Once used, a real object will be placed in the scope.

        :param scope: The scope the object should appear in
        :param factory: A callable that will create the real object.
            It will be passed (self, scope, name)
        :param name: The variable name in the given scope.
        """
        object.__setattr__(self, '_scope', scope)
        object.__setattr__(self, '_factory', factory)
        object.__setattr__(self, '_name', name)
        object.__setattr__(self, '_real_obj', None)
        scope[name] = self

    def __setattr__(self, attr, value):
        obj = object.__getattribute__(self, '_resolve')()
        return setattr(obj, attr, value)


class ScopeReplacer(object):

    __slots__ = ('_scope', '_factory', '_name', '_real_obj')

    _should_proxy = True

    def __init__(self, scope, factory, name):
        """Create a temporary object in the specified scope.
        Once used, a real object will be placed in the scope.

        :param scope: The scope the object should appear in
        :param factory: A callable that will create the real object.
            It will be passed (self, scope, name)
        :param name: The variable name in the given scope.
        """
        object.__setattr__(self, '_scope', scope)
        object.__setattr__(self, '_factory', factory)
        object.__setattr__(self, '_name', name)
        object.__setattr__(self, '_real_obj', None)
        scope[name] = self

    def __call__(self, *args, **kwargs):
        obj = object.__getattribute__(self, '_resolve')()
        return obj(*args, **kwargs)


import re

_real_re_compile = re.compile

class InvalidPattern(ValueError):

    _fmt = ('Invalid pattern(s) found. %(msg)s')

    def __init__(self, msg):
        self.msg = msg

    def __unicode__(self):
        u = self._format()
        if isinstance(u, str):
            # Try decoding the str using the default encoding.
            u = unicode(u)
        elif not isinstance(u, unicode):
            # Try to make a unicode object from it, because __unicode__ must
            # return a unicode object.
            u = unicode(u)
        return u


import re

_real_re_compile = re.compile

class InvalidPattern(ValueError):

    _fmt = ('Invalid pattern(s) found. %(msg)s')

    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        s = self._format()
        if isinstance(s, unicode):
            s = s.encode('utf8')
        else:
            # __str__ must return a str.
            s = str(s)
        return s


import re

_real_re_compile = re.compile

class LazyRegex(object):

    _regex_attributes_to_copy = [
                 '__copy__', '__deepcopy__', 'findall', 'finditer', 'match',
                 'scanner', 'search', 'split', 'sub', 'subn'
                 ]

    __slots__ = ['_real_regex', '_regex_args', '_regex_kwargs',
                ] + _regex_attributes_to_copy

    def __init__(self, args=(), kwargs={}):
        """Create a new proxy object, passing in the args to pass to re.compile

        :param args: The `*args` to pass to re.compile
        :param kwargs: The `**kwargs` to pass to re.compile
        """
        self._real_regex = None
        self._regex_args = args
        self._regex_kwargs = kwargs

    def __setstate__(self, dict):
        """Restore from a pickled state."""
        self._real_regex = None
        setattr(self, "_regex_args", dict["args"])
        setattr(self, "_regex_kwargs", dict["kwargs"])


import re

_real_re_compile = re.compile

class LazyRegex(object):

    _regex_attributes_to_copy = [
                 '__copy__', '__deepcopy__', 'findall', 'finditer', 'match',
                 'scanner', 'search', 'split', 'sub', 'subn'
                 ]

    __slots__ = ['_real_regex', '_regex_args', '_regex_kwargs',
                ] + _regex_attributes_to_copy

    def __init__(self, args=(), kwargs={}):
        """Create a new proxy object, passing in the args to pass to re.compile

        :param args: The `*args` to pass to re.compile
        :param kwargs: The `**kwargs` to pass to re.compile
        """
        self._real_regex = None
        self._regex_args = args
        self._regex_kwargs = kwargs

    def __getattr__(self, attr):
        """Return a member from the proxied regex object.

        If the regex hasn't been compiled yet, compile it
        """
        if self._real_regex is None:
            self._compile_and_collapse()
        # Once we have compiled, the only time we should come here
        # is actually if the attribute is missing.
        return getattr(self._real_regex, attr)


import sys
from types import ModuleType

class NonLocal(object):

    __slots__ = ['value']

    def __init__(self, value):
        self.value = value

def make_lazy(module_path):
    """
    Mark that this module should not be imported until an
    attribute is needed off of it.
    """
    sys_modules = sys.modules  # cache in the locals

    # store our 'instance' data in the closure.
    module = NonLocal(None)

    class LazyModule(_LazyModuleMarker):
        """
        A standin for a module to prevent it from being imported
        """
        def __mro__(self):
            """
            Override the __mro__ to fool `isinstance`.
            """
            # We don't use direct subclassing because `ModuleType` has an
            # incompatible metaclass base with object (they are both in c)
            # and we are overridding __getattribute__.
            # By putting a __mro__ method here, we can pass `isinstance`
            # checks without ever invoking our __getattribute__ function.
            return (LazyModule, ModuleType)

        def __getattribute__(self, attr):
            """
            Override __getattribute__ to hide the implementation details.
            """
            if module.value is None:
                del sys_modules[module_path]
                module.value = __import__(module_path)

                sys_modules[module_path] = __import__(module_path)

            return getattr(module.value, attr)

    sys_modules[module_path] = LazyModule()


import logging
import logging.config
import inspect
import sys
import os
from contextlib import contextmanager

DEFAULT_CONFIG = dict(
    version=1,
    disable_existing_loggers=False,
    formatters={
        'colored': {
            '()': 'colorlog.ColoredFormatter',
            'format':
                '%(bg_black)s%(log_color)s'
                '[%(asctime)s] '
                '[%(name)s/%(process)d] '
                '%(message)s '
                '%(blue)s@%(funcName)s:%(lineno)d '
                '#%(levelname)s'
                '%(reset)s',
            'datefmt': '%H:%M:%S',
        },
        'simple': {
            # format=' '.join(
            #     [
            #         '%(asctime)s|',
            #         '%(name)s/%(processName)s[%(process)d]-%(threadName)s[%(thread)d]:'
            #         '%(message)s @%(funcName)s:%(lineno)d #%(levelname)s',
            #     ]
            # ),
            'format':
                '%(asctime)s| %(name)s/%(processName)s[%(process)d]-%(threadName)s[%(thread)d]: '
                '%(message)s @%(funcName)s:%(lineno)d #%(levelname)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
    },
    handlers={
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'colored',
            'level': logging.DEBUG,
        },
    },
    root=dict(handlers=['console'], level=logging.DEBUG),
    loggers={
        'requests': dict(level=logging.INFO),
    },
)
_CONFIGURED = []
getLogger = get_logger

def configure(config=None, env_var='LOGGING', default=DEFAULT_CONFIG):
    """

    >>> log = logging.getLogger(__name__)
    >>> configure()
    >>> log.info('test')

    """
    cfg = get_config(config, env_var, default)

    try:
        logging.config.dictConfig(cfg)
    except TypeError as exc:
        try:
            logging.basicConfig(**cfg)
        except Exception as inner_exc:
            raise inner_exc from exc


import logging
import logging.config
import inspect
import sys
import os
from contextlib import contextmanager

DEFAULT_CONFIG = dict(
    version=1,
    disable_existing_loggers=False,
    formatters={
        'colored': {
            '()': 'colorlog.ColoredFormatter',
            'format':
                '%(bg_black)s%(log_color)s'
                '[%(asctime)s] '
                '[%(name)s/%(process)d] '
                '%(message)s '
                '%(blue)s@%(funcName)s:%(lineno)d '
                '#%(levelname)s'
                '%(reset)s',
            'datefmt': '%H:%M:%S',
        },
        'simple': {
            # format=' '.join(
            #     [
            #         '%(asctime)s|',
            #         '%(name)s/%(processName)s[%(process)d]-%(threadName)s[%(thread)d]:'
            #         '%(message)s @%(funcName)s:%(lineno)d #%(levelname)s',
            #     ]
            # ),
            'format':
                '%(asctime)s| %(name)s/%(processName)s[%(process)d]-%(threadName)s[%(thread)d]: '
                '%(message)s @%(funcName)s:%(lineno)d #%(levelname)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
    },
    handlers={
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'colored',
            'level': logging.DEBUG,
        },
    },
    root=dict(handlers=['console'], level=logging.DEBUG),
    loggers={
        'requests': dict(level=logging.INFO),
    },
)
_CONFIGURED = []
getLogger = get_logger

def get_config(given=None, env_var=None, default=None):
    config = given

    if not config and env_var:
        config = os.environ.get(env_var)

    if not config and default:
        config = default

    if config is None:
        raise ValueError('Invalid logging config: %s' % config)

    if isinstance(config, _PyInfo.string_types):
        import json

        try:
            config = json.loads(config)
        except ValueError:
            import yaml

            try:
                config = yaml.load(config)
            except ValueError:
                raise ValueError(
                    "Could not parse logging config as bare, json,"
                    " or yaml: %s" % config
                )

    return config


import os

def join_each(parent, iterable):
    for p in iterable:
        yield os.path.join(parent, p)


classproperty = roclassproperty

def lazyperclassproperty(fn):
    """
    Lazy/Cached class property that stores separate instances per class/inheritor so there's no overlap.
    """

    @classproperty
    def _lazyclassprop(cls):
        attr_name = '_%s_lazy_%s' % (cls.__name__, fn.__name__)
        if not hasattr(cls, attr_name):
            setattr(cls, attr_name, fn(cls))
        return getattr(cls, attr_name)

    return _lazyclassprop


classproperty = roclassproperty

def lazyclassproperty(fn):
    """
    Lazy/Cached class property.
    """
    attr_name = '_lazy_' + fn.__name__

    @classproperty
    def _lazyclassprop(cls):
        if not hasattr(cls, attr_name):
            setattr(cls, attr_name, fn(cls))
        return getattr(cls, attr_name)

    return _lazyclassprop


import collections

_sentinel = object()

def get_tree_node(mapping, key, default=_sentinel, parent=False):
    """
    Fetch arbitrary node from a tree-like mapping structure with traversal help:
    Dimension can be specified via ':'

    Arguments:
        mapping collections.Mapping: Mapping to fetch from
        key str|unicode: Key to lookup, allowing for : notation
        default object: Default value. If set to `:module:_sentinel`, raise KeyError if not found.
        parent bool: If True, return parent node. Defaults to False.

    Returns:
        object: Value at specified key
    """
    key = key.split(':')
    if parent:
        key = key[:-1]

    # TODO Unlist my shit. Stop calling me please.

    node = mapping
    for node in key.split(':'):
        try:
            node = node[node]
        except KeyError as exc:
            node = default
            break

    if node is _sentinel:
        raise exc
    return node


import collections

_sentinel = object()

def set_tree_node(mapping, key, value):
    """
    Set arbitrary node on a tree-like mapping structure, allowing for : notation to signify dimension.

    Arguments:
        mapping collections.Mapping: Mapping to fetch from
        key str|unicode: Key to set, allowing for : notation
        value str|unicode: Value to set `key` to
        parent bool: If True, return parent node. Defaults to False.

    Returns:
        object: Parent node.

    """
    basename, dirname = key.rsplit(':', 2)
    parent_node = get_tree_node(mapping, dirname)
    parent_node[basename] = value
    return parent_node


import collections

_sentinel = object()

class Tree(collections.defaultdict):

    namespace = None

    get = __getitem__

    def __init__(self, initial=None, namespace='', initial_is_ref=False):
        if initial is not None and initial_is_ref:
            self.data = initial_is_ref
        self.namespace = namespace
        super(Tree, self).__init__(self.__class__)
        if initial is not None:
            self.update(initial)


def update_query_params(url, params, doseq=True):
    """
    Update and/or insert query parameters in a URL.

    >>> update_query_params('http://example.com?foo=bar&biz=baz', dict(foo='stuff'))
    'http://example.com?...foo=stuff...'

    :param url: URL
    :type url: str
    :param kwargs: Query parameters
    :type kwargs: dict
    :return: Modified URL
    :rtype: str
    """
    scheme, netloc, path, query_string, fragment = urlparse.urlsplit(url)

    query_params = urlparse.parse_qs(query_string)
    query_params.update(**params)

    new_query_string = urlencode(query_params, doseq=doseq)

    new_url = urlparse.urlunsplit([scheme, netloc, path, new_query_string, fragment])
    return new_url


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def __iter__(self):
        """
        Tun the class Blueprint Group into an Iterable item
        """
        return iter(self._blueprints)


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def __getitem__(self, item):
        """
        This method returns a blueprint inside the group specified by
        an index value. This will enable indexing, splice and slicing
        of the blueprint group like we can do with regular list/tuple.

        This method is provided to ensure backward compatibility with
        any of the pre-existing usage that might break.

        :param item: Index of the Blueprint item in the group
        :return: Blueprint object
        """
        return self._blueprints[item]


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def __setitem__(self, index, item) -> None:
        """
        Abstract method implemented to turn the `BlueprintGroup` class
        into a list like object to support all the existing behavior.

        This method is used to perform the list's indexed setter operation.

        :param index: Index to use for inserting a new Blueprint item
        :param item: New `Blueprint` object.
        :return: None
        """
        self._blueprints[index] = item


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def __delitem__(self, index) -> None:
        """
        Abstract method implemented to turn the `BlueprintGroup` class
        into a list like object to support all the existing behavior.

        This method is used to delete an item from the list of blueprint
        groups like it can be done on a regular list with index.

        :param index: Index to use for removing a new Blueprint item
        :return: None
        """
        del self._blueprints[index]


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def __len__(self) -> int:
        """
        Get the Length of the blueprint group object.

        :return: Length of Blueprint group object
        """
        return len(self._blueprints)


from collections.abc import MutableSequence
from typing import List, Optional, Union
import sanic

class BlueprintGroup(MutableSequence):

    __slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")

    def __init__(self, url_prefix=None, version=None, strict_slashes=None):
        """
        Create a new Blueprint Group

        :param url_prefix: URL: to be prefixed before all the Blueprint Prefix
        :param version: API Version for the blueprint group. This will be
            inherited by each of the Blueprint
        :param strict_slashes: URL Strict slash behavior indicator
        """
        self._blueprints = []
        self._url_prefix = url_prefix
        self._version = version
        self._strict_slashes = strict_slashes

    def append(self, value: "sanic.Blueprint") -> None:
        """
        The Abstract class `MutableSequence` leverages this append method to
        perform the `BlueprintGroup.append` operation.
        :param value: New `Blueprint` object.
        :return: None
        """
        self._blueprints.append(self._sanitize_blueprint(bp=value))