<commit_before>from __future__ import unicode_literals

import uritemplate
import wac

from balanced import exc, config, utils

registry = wac.ResourceRegistry(route_prefix='/')

class JSONSchemaCollection(wac.ResourceCollection):

    @property
    def href(self):
        return self.uri

class ObjectifyMixin(wac._ObjectifyMixin):

    def _objectify(self, resource_cls, **fields):
        # setting values locally, not from server
        if 'links' not in fields:
            for key, value in fields.iteritems():
                setattr(self, key, value)
        else:
            self._construct_from_response(**fields)

    def _construct_from_response(self, **payload):
        payload = self._hydrate(payload)
        meta = payload.pop('meta', None)

        if isinstance(self, wac.Page):
            for key, value in meta.iteritems():
                setattr(self, key, value)

        # the remaining keys here are just hypermedia resources
        for _type, resources in payload.iteritems():
            # Singular resources are represented as JSON objects. However,
            # they are still wrapped inside an array:
            cls = Resource.registry[_type]

            for resource_body in resources:
                # if we couldn't determine the type of this object we use a
                # generic resource object, target that instead.
                if isinstance(self, (cls, Resource)):
                    # we are loading onto our self, self is the target
                    target = self
                else:
                    target = cls()
                for key, value in resource_body.iteritems():
                    if key in ('links',):
                        continue
                    setattr(target, key, value)

                # if loading into a collection
                if target != self:
                    # ensure that we have a collection to hold this item
                    if not hasattr(self, _type):
                        setattr(self, _type, [])
                    getattr(self, _type).append(target)

    @classmethod
    def _hydrate(cls, payload):
        """
        Construct links for objects
        """
        links = payload.pop('links', {})
        for key, uri in links.iteritems():
            variables = uritemplate.variables(uri)
            # marketplaces.card_holds
            collection, resource_type = key.split('.')
            item_attribute = item_property = resource_type
            # if parsed from uri then retrieve. e.g. customer.id
            for item in payload[collection]:
                # find type, fallback to Resource if we can't determine the
                # type e.g. marketplace.owner_customer
                collection_type = Resource.registry.get(resource_type,
                                                        Resource)

                def extract_variables_from_item(item, variables):
                    for v in variables:
                        _, item_attribute = v.split('.')
                        # HACK: https://github.com/PoundPay/balanced/issues/184
                        if item_attribute == 'self':
                            item_attribute = 'id'
                        item_value = item['links'].get(
                            item_attribute, item.get(item_attribute)
                        )
                        if item_value:
                            yield v, item_value

                item_variables = dict(
                    extract_variables_from_item(item, variables))

                # expand variables if we have them, else this is a link like
                # /debits
                if item_variables:
                    parsed_link = uritemplate.expand(uri, item_variables)
                else:
                    parsed_link = uri

                # check if this is a collection or a singular item
                if any(
                        parsed_link.endswith(value)
                        for value in item_variables.itervalues()
                ):
                    # singular
                    if not item_property.endswith('_href'):
                        item_property += '_href'
                    lazy_href = parsed_link

                elif '{' in parsed_link and '}' in parsed_link:
                    # the link is of the form /asdf/{asdf} which means
                    # that the variables could not be resolved as it
                    # was None.  Instead of making it into a page object
                    # we explicitly set it to None to represent the
                    # attribute is None
                    lazy_href = None
                else:
                    # collection
                    lazy_href = JSONSchemaCollection(
                        collection_type, parsed_link)
                item.setdefault(item_property, lazy_href)
        return payload

class JSONSchemaPage(wac.Page, ObjectifyMixin):

    @property
    def items(self):
        try:
            try:
                return getattr(self, self.resource_cls.type)
            except AttributeError:
                # horrid hack because event callbacks are misnamed.
                return self.event_callbacks
        except AttributeError:
            # Notice:
            # there is no resources key in the response from server
            # if the list is empty, so when we try to get something like
            # `debits`, an AttributeError will be raised. Not sure is this
            # behavior a bug of server, but anyway, this is just a workaround
            # here for solving the problem. The issue was posted here
            # https://github.com/balanced/balanced-python/issues/93
            return []

class JSONSchemaResource(wac.Resource, ObjectifyMixin):

    collection_cls = JSONSchemaCollection

    page_cls = JSONSchemaPage

    def save(self):
        cls = type(self)
        attrs = self.__dict__.copy()
        href = attrs.pop('href', None)

        if not href:
            if not cls.uri_gen or not cls.uri_gen.root_uri:
                raise TypeError(
                    'Unable to create {0} resources directly'.format(
                        cls.__name__
                    )
                )
            href = cls.uri_gen.root_uri

        method = cls.client.put if 'id' in attrs else cls.client.post

        attrs = dict(
            (k, v.href if isinstance(v, Resource) else v)
            for k, v in attrs.iteritems()
            if not isinstance(v, (cls.collection_cls))
        )

        resp = method(href, data=attrs)

        instance = self.__class__(**resp.data)
        self.__dict__.clear()
        self.__dict__.update(instance.__dict__)

        return self

    def delete(self):
        self.client.delete(self.href)

    def __dir__(self):
        return self.__dict__.keys()

    def __getattr__(self, item):
        if isinstance(item, basestring):
            suffix = '_href'
            if suffix not in item:
                href = getattr(self, item + suffix, None)
                if href:
                    item_type = Resource.registry.get(item + 's', Resource)
                    setattr(self, item, item_type.get(href))
                    return getattr(self, item)
        raise AttributeError(
            "'{0}' has no attribute '{1}'".format(
                self.__class__.__name__, item
            )
        )

class Resource(JSONSchemaResource):

    client = config.client

    registry = registry

    uri_gen = wac.URIGen('/resources', '{resource}')

    def unstore(self):
        return self.delete()

    @classmethod
    def fetch(cls, href):
        return cls.get(href)

    @classmethod
    def get(cls, href):
        if href.startswith('/resources'):
            # hackety hack hax
            # resource is an abstract type, we shouldn't have it comeing back itself
            # instead we need to figure out the type based off the api response
            resp = cls.client.get(href)
            resource = [
                k for k in resp.data.keys() if k != 'links' and k != 'meta'
            ]
            if resource:
                return Resource.registry.get(resource[0], cls)(**resp.data)
            return cls(**resp.data)
        return super(Resource, cls).get(href)

class Marketplace(Resource):
    """
    A Marketplace represents your central broker for all operations on the
    Balanced API.

    A Marketplace has a single `owner_customer` which represents your person or
    business.

    All Resources apart from APIKeys are associated with a Marketplace.

    A Marketplace has an escrow account which receives all funds from Debits
    that are not associated with Orders. The sum of the escrow (`in_escrow`) is
    (Debits - Refunds + Reversals - Credits).
    """

    type = 'marketplaces'

    uri_gen = wac.URIGen('/marketplaces', '{marketplace}')

    @utils.classproperty
    def mine(cls):
        """
        Returns an instance representing the marketplace associated with the
        current API key used for this request.
        """
        return cls.query.one()

    my_marketplace = mine

class APIKey(Resource):
    """
    Your APIKey is used to authenticate when performing operations on the
    Balanced API. You must create an APIKey before you create a Marketplace.

    **NOTE:** Never give out or expose your APIKey. You may POST to this
    endpoint to create new APIKeys and then DELETE any old keys.
    """
    type = 'api_keys'

    uri_gen = wac.URIGen('/api_keys', '{api_key}')

class CardHold(Resource):

    type = 'card_holds'

    uri_gen = wac.URIGen('/card_holds', '{card_hold}')

    def cancel(self):
        self.is_void = False
        return self.save()

    def capture(self, **kwargs):
        return Debit(
            href=self.debits.href,
            **kwargs
        ).save()

class Transaction(Resource):
    """
    Any transfer, funds from or to, your Marketplace's escrow account or the
    escrow account of an Order associated with your Marketplace.
    E.g. a Credit, Debit, Refund, or Reversal.

    If the Transaction is associated with an Order then it will be applied to
    the Order's escrow account, not to the Marketplace's escrow account.
    """

    type = 'transactions'

class Credit(Transaction):
    """
    A Credit represents a transfer of funds from your Marketplace's
    escrow account to a FundingInstrument.

    Credits are created by calling the `credit` method on a FundingInstrument.
    """

    type = 'credits'

    uri_gen = wac.URIGen('/credits', '{credit}')

    def reverse(self, **kwargs):
        """
        Reverse a Credit.  If no amount is specified it will reverse the entire
        amount of the Credit, you may create many Reversals up to the sum of
        the total amount of the original Credit.

        :rtype: Reversal
        """
        return Reversal(
            href=self.reversals.href,
            **kwargs
        ).save()

class Debit(Transaction):
    """
    A Debit represents a transfer of funds from a FundingInstrument to your
    Marketplace's escrow account.

    A Debit may be created directly, or it will be created as a side-effect
    of capturing a CardHold. If you create a Debit directly it will implicitly
    create the associated CardHold if the FundingInstrument supports this.
    """

    type = 'debits'

    uri_gen = wac.URIGen('/debits', '{debit}')

    def refund(self, **kwargs):
        """
        Refunds this Debit. If no amount is specified it will refund the entire
        amount of the Debit, you may create many Refunds up to the sum total
        of the original Debit's amount.

        :rtype: Refund
        """
        return Refund(
            href=self.refunds.href,
            **kwargs
        ).save()

class Refund(Transaction):
    """
    A Refund represents a reversal of funds from a Debit. A Debit can have
    many Refunds associated with it up to the total amount of the original
    Debit. Funds are returned to your Marketplace's escrow account
    proportional to the amount of the Refund.
    """

    type = 'refunds'

    uri_gen = wac.URIGen('/refunds', '{refund}')

class Reversal(Transaction):
    """
    A Reversal represents a reversal of funds from a Credit. A Credit can have
    many Reversal associated with it up to the total amount of the original
    Credit. Funds are returned to your Marketplace's escrow account
    proportional to the amount of the Reversal.
    """

    type = 'reversals'

    uri_gen = wac.URIGen('/reversals', '{reversal}')

class FundingInstrument(Resource):
    """
    A FundingInstrument is either (or both) a source or destination of funds.
    You may perform `debit` or `credit` operations on a FundingInstrument to
    transfer funds to or from your Marketplace's escrow.
    """

    type = 'funding_instruments'

    def associate_to_customer(self, customer):
        try:
            self.links
        except AttributeError:
            self.links = {}
        self.links['customer'] = utils.extract_href_from_object(customer)
        self.save()

    def debit(self, amount, **kwargs):
        """
        Creates a Debit of funds from this FundingInstrument to your
        Marketplace's escrow account.

        :param appears_on_statement_as: If None then Balanced will use the
            `domain_name` property from your Marketplace.
        :rtype: Debit
        """
        return Debit(
            href=self.debits.href,
            amount=amount,
            **kwargs
        ).save()

    def credit(self, amount, **kwargs):
        """
        Creates a Credit of funds from your Marketplace's escrow account to
        this FundingInstrument.

        :rtype: Credit
        """

        if hasattr(self, 'credits'):
            return Credit(
                href=self.credits.href,
                amount=amount,
                **kwargs
            ).save()
        else:
            raise exc.FundingSourceNotCreditable

class BankAccount(FundingInstrument):
    """
    A BankAccount is both a source, and a destination of, funds. You may
    create Debits and Credits to and from, this funding instrument.
    """

    type = 'bank_accounts'

    uri_gen = wac.URIGen('/bank_accounts', '{bank_account}')

    def verify(self):
        """
        Creates a verification of the associated BankAccount so it can
        perform verified operations (debits).

        :rtype: BankAccountVerification
        """
        return BankAccountVerification(
            href=self.bank_account_verifications.href
        ).save()

class BankAccountVerification(Resource):
    """
    Represents an attempt to verify the associated BankAccount so it can
    perform verified operations (debits).
    """

    type = 'bank_account_verifications'

    def confirm(self, amount_1, amount_2):
        self.amount_1 = amount_1
        self.amount_2 = amount_2
        return self.save()

class Card(FundingInstrument):
    """
    A card represents a source of funds. You may Debit funds from the Card.
    """

    type = 'cards'

    uri_gen = wac.URIGen('/cards', '{card}')

    def hold(self, amount, **kwargs):
        return CardHold(
            href=self.card_holds.href,
            amount=amount,
            **kwargs
        ).save()

class Customer(Resource):
    """
    A Customer represents a business or person within your Marketplace. A
    Customer can have many funding instruments such as cards and bank accounts
    associated to them. Customers are logical grouping constructs for
    associating many Transactions and FundingInstruments.
    """

    type = 'customers'

    uri_gen = wac.URIGen('/customers', '{customer}')

    def create_order(self, **kwargs):
        return Order(href=self.orders.href, **kwargs).save()

class Order(Resource):
    """
    An Order is a logical construct for grouping Transactions.

    An Order may have 0:n Transactions associated with it so long as the sum
    (`amount_escrowed`) which is calculated as
    (Debits - Refunds - Credits + Reversals), is always >= 0.
    """

    type = 'orders'

    uri_gen = wac.URIGen('/orders', '{order}')

    def credit_to(self, destination, amount, **kwargs):
        return destination.credit(order=self.href,
                                  amount=amount,
                                  **kwargs)

    def debit_from(self, source, amount, **kwargs):
        return source.debit(order=self.href,
                            amount=amount,
                            **kwargs)

class Callback(Resource):
    """
    A Callback is a publicly accessible location that can receive POSTed JSON
    data whenever an Event is generated.
    """

    type = 'callbacks'

    uri_gen = wac.URIGen('/callbacks', '{callback}')

class Dispute(Resource):
    """
    A dispute occurs when a customer disputes a transaction that
    occurred on their funding instrument.
    """
    type = 'disputes'

    uri_gen = wac.URIGen('/disputes', '{dispute}')

class Event(Resource):
    """
    An Event is a snapshot of another resource at a point in time when
    something significant occurred. Events are created when resources are
    created, updated, deleted or otherwise change state such as a Credit being
    marked as failed.
    """

    type = 'events'

    uri_gen = wac.URIGen('/events', '{event}')

class EventCallback(Resource):
    """
    Represents a single event being sent to a callback.
    """

    type = 'event_callbacks'

class EventCallbackLog(Resource):
    """
    Represents a request and response from single attempt to notify a callback
    of an event.
    """

    type = 'event_callback_logs'

class ExternalAccount(FundingInstrument):
    """
    An External Account represents a source of funds provided by an external, 3rd
    party processor. You may Debit funds from the account if can_debit is true.
    """

    type = 'external_accounts'

    uri_gen = wac.URIGen('/external_accounts', '{external_account}')
<commit_msg>Raise if not. Remove else.<commit_after>from __future__ import unicode_literals

import uritemplate
import wac

from balanced import exc, config, utils

registry = wac.ResourceRegistry(route_prefix='/')

class JSONSchemaCollection(wac.ResourceCollection):

    @property
    def href(self):
        return self.uri

class ObjectifyMixin(wac._ObjectifyMixin):

    def _objectify(self, resource_cls, **fields):
        # setting values locally, not from server
        if 'links' not in fields:
            for key, value in fields.iteritems():
                setattr(self, key, value)
        else:
            self._construct_from_response(**fields)

    def _construct_from_response(self, **payload):
        payload = self._hydrate(payload)
        meta = payload.pop('meta', None)

        if isinstance(self, wac.Page):
            for key, value in meta.iteritems():
                setattr(self, key, value)

        # the remaining keys here are just hypermedia resources
        for _type, resources in payload.iteritems():
            # Singular resources are represented as JSON objects. However,
            # they are still wrapped inside an array:
            cls = Resource.registry[_type]

            for resource_body in resources:
                # if we couldn't determine the type of this object we use a
                # generic resource object, target that instead.
                if isinstance(self, (cls, Resource)):
                    # we are loading onto our self, self is the target
                    target = self
                else:
                    target = cls()
                for key, value in resource_body.iteritems():
                    if key in ('links',):
                        continue
                    setattr(target, key, value)

                # if loading into a collection
                if target != self:
                    # ensure that we have a collection to hold this item
                    if not hasattr(self, _type):
                        setattr(self, _type, [])
                    getattr(self, _type).append(target)

    @classmethod
    def _hydrate(cls, payload):
        """
        Construct links for objects
        """
        links = payload.pop('links', {})
        for key, uri in links.iteritems():
            variables = uritemplate.variables(uri)
            # marketplaces.card_holds
            collection, resource_type = key.split('.')
            item_attribute = item_property = resource_type
            # if parsed from uri then retrieve. e.g. customer.id
            for item in payload[collection]:
                # find type, fallback to Resource if we can't determine the
                # type e.g. marketplace.owner_customer
                collection_type = Resource.registry.get(resource_type,
                                                        Resource)

                def extract_variables_from_item(item, variables):
                    for v in variables:
                        _, item_attribute = v.split('.')
                        # HACK: https://github.com/PoundPay/balanced/issues/184
                        if item_attribute == 'self':
                            item_attribute = 'id'
                        item_value = item['links'].get(
                            item_attribute, item.get(item_attribute)
                        )
                        if item_value:
                            yield v, item_value

                item_variables = dict(
                    extract_variables_from_item(item, variables))

                # expand variables if we have them, else this is a link like
                # /debits
                if item_variables:
                    parsed_link = uritemplate.expand(uri, item_variables)
                else:
                    parsed_link = uri

                # check if this is a collection or a singular item
                if any(
                        parsed_link.endswith(value)
                        for value in item_variables.itervalues()
                ):
                    # singular
                    if not item_property.endswith('_href'):
                        item_property += '_href'
                    lazy_href = parsed_link

                elif '{' in parsed_link and '}' in parsed_link:
                    # the link is of the form /asdf/{asdf} which means
                    # that the variables could not be resolved as it
                    # was None.  Instead of making it into a page object
                    # we explicitly set it to None to represent the
                    # attribute is None
                    lazy_href = None
                else:
                    # collection
                    lazy_href = JSONSchemaCollection(
                        collection_type, parsed_link)
                item.setdefault(item_property, lazy_href)
        return payload

class JSONSchemaPage(wac.Page, ObjectifyMixin):

    @property
    def items(self):
        try:
            try:
                return getattr(self, self.resource_cls.type)
            except AttributeError:
                # horrid hack because event callbacks are misnamed.
                return self.event_callbacks
        except AttributeError:
            # Notice:
            # there is no resources key in the response from server
            # if the list is empty, so when we try to get something like
            # `debits`, an AttributeError will be raised. Not sure is this
            # behavior a bug of server, but anyway, this is just a workaround
            # here for solving the problem. The issue was posted here
            # https://github.com/balanced/balanced-python/issues/93
            return []

class JSONSchemaResource(wac.Resource, ObjectifyMixin):

    collection_cls = JSONSchemaCollection

    page_cls = JSONSchemaPage

    def save(self):
        cls = type(self)
        attrs = self.__dict__.copy()
        href = attrs.pop('href', None)

        if not href:
            if not cls.uri_gen or not cls.uri_gen.root_uri:
                raise TypeError(
                    'Unable to create {0} resources directly'.format(
                        cls.__name__
                    )
                )
            href = cls.uri_gen.root_uri

        method = cls.client.put if 'id' in attrs else cls.client.post

        attrs = dict(
            (k, v.href if isinstance(v, Resource) else v)
            for k, v in attrs.iteritems()
            if not isinstance(v, (cls.collection_cls))
        )

        resp = method(href, data=attrs)

        instance = self.__class__(**resp.data)
        self.__dict__.clear()
        self.__dict__.update(instance.__dict__)

        return self

    def delete(self):
        self.client.delete(self.href)

    def __dir__(self):
        return self.__dict__.keys()

    def __getattr__(self, item):
        if isinstance(item, basestring):
            suffix = '_href'
            if suffix not in item:
                href = getattr(self, item + suffix, None)
                if href:
                    item_type = Resource.registry.get(item + 's', Resource)
                    setattr(self, item, item_type.get(href))
                    return getattr(self, item)
        raise AttributeError(
            "'{0}' has no attribute '{1}'".format(
                self.__class__.__name__, item
            )
        )

class Resource(JSONSchemaResource):

    client = config.client

    registry = registry

    uri_gen = wac.URIGen('/resources', '{resource}')

    def unstore(self):
        return self.delete()

    @classmethod
    def fetch(cls, href):
        return cls.get(href)

    @classmethod
    def get(cls, href):
        if href.startswith('/resources'):
            # hackety hack hax
            # resource is an abstract type, we shouldn't have it comeing back itself
            # instead we need to figure out the type based off the api response
            resp = cls.client.get(href)
            resource = [
                k for k in resp.data.keys() if k != 'links' and k != 'meta'
            ]
            if resource:
                return Resource.registry.get(resource[0], cls)(**resp.data)
            return cls(**resp.data)
        return super(Resource, cls).get(href)

class Marketplace(Resource):
    """
    A Marketplace represents your central broker for all operations on the
    Balanced API.

    A Marketplace has a single `owner_customer` which represents your person or
    business.

    All Resources apart from APIKeys are associated with a Marketplace.

    A Marketplace has an escrow account which receives all funds from Debits
    that are not associated with Orders. The sum of the escrow (`in_escrow`) is
    (Debits - Refunds + Reversals - Credits).
    """

    type = 'marketplaces'

    uri_gen = wac.URIGen('/marketplaces', '{marketplace}')

    @utils.classproperty
    def mine(cls):
        """
        Returns an instance representing the marketplace associated with the
        current API key used for this request.
        """
        return cls.query.one()

    my_marketplace = mine

class APIKey(Resource):
    """
    Your APIKey is used to authenticate when performing operations on the
    Balanced API. You must create an APIKey before you create a Marketplace.

    **NOTE:** Never give out or expose your APIKey. You may POST to this
    endpoint to create new APIKeys and then DELETE any old keys.
    """
    type = 'api_keys'

    uri_gen = wac.URIGen('/api_keys', '{api_key}')

class CardHold(Resource):

    type = 'card_holds'

    uri_gen = wac.URIGen('/card_holds', '{card_hold}')

    def cancel(self):
        self.is_void = False
        return self.save()

    def capture(self, **kwargs):
        return Debit(
            href=self.debits.href,
            **kwargs
        ).save()

class Transaction(Resource):
    """
    Any transfer, funds from or to, your Marketplace's escrow account or the
    escrow account of an Order associated with your Marketplace.
    E.g. a Credit, Debit, Refund, or Reversal.

    If the Transaction is associated with an Order then it will be applied to
    the Order's escrow account, not to the Marketplace's escrow account.
    """

    type = 'transactions'

class Credit(Transaction):
    """
    A Credit represents a transfer of funds from your Marketplace's
    escrow account to a FundingInstrument.

    Credits are created by calling the `credit` method on a FundingInstrument.
    """

    type = 'credits'

    uri_gen = wac.URIGen('/credits', '{credit}')

    def reverse(self, **kwargs):
        """
        Reverse a Credit.  If no amount is specified it will reverse the entire
        amount of the Credit, you may create many Reversals up to the sum of
        the total amount of the original Credit.

        :rtype: Reversal
        """
        return Reversal(
            href=self.reversals.href,
            **kwargs
        ).save()

class Debit(Transaction):
    """
    A Debit represents a transfer of funds from a FundingInstrument to your
    Marketplace's escrow account.

    A Debit may be created directly, or it will be created as a side-effect
    of capturing a CardHold. If you create a Debit directly it will implicitly
    create the associated CardHold if the FundingInstrument supports this.
    """

    type = 'debits'

    uri_gen = wac.URIGen('/debits', '{debit}')

    def refund(self, **kwargs):
        """
        Refunds this Debit. If no amount is specified it will refund the entire
        amount of the Debit, you may create many Refunds up to the sum total
        of the original Debit's amount.

        :rtype: Refund
        """
        return Refund(
            href=self.refunds.href,
            **kwargs
        ).save()

class Refund(Transaction):
    """
    A Refund represents a reversal of funds from a Debit. A Debit can have
    many Refunds associated with it up to the total amount of the original
    Debit. Funds are returned to your Marketplace's escrow account
    proportional to the amount of the Refund.
    """

    type = 'refunds'

    uri_gen = wac.URIGen('/refunds', '{refund}')

class Reversal(Transaction):
    """
    A Reversal represents a reversal of funds from a Credit. A Credit can have
    many Reversal associated with it up to the total amount of the original
    Credit. Funds are returned to your Marketplace's escrow account
    proportional to the amount of the Reversal.
    """

    type = 'reversals'

    uri_gen = wac.URIGen('/reversals', '{reversal}')

class FundingInstrument(Resource):
    """
    A FundingInstrument is either (or both) a source or destination of funds.
    You may perform `debit` or `credit` operations on a FundingInstrument to
    transfer funds to or from your Marketplace's escrow.
    """

    type = 'funding_instruments'

    def associate_to_customer(self, customer):
        try:
            self.links
        except AttributeError:
            self.links = {}
        self.links['customer'] = utils.extract_href_from_object(customer)
        self.save()

    def debit(self, amount, **kwargs):
        """
        Creates a Debit of funds from this FundingInstrument to your
        Marketplace's escrow account.

        :param appears_on_statement_as: If None then Balanced will use the
            `domain_name` property from your Marketplace.
        :rtype: Debit
        """
        return Debit(
            href=self.debits.href,
            amount=amount,
            **kwargs
        ).save()

    def credit(self, amount, **kwargs):
        """
        Creates a Credit of funds from your Marketplace's escrow account to
        this FundingInstrument.

        :rtype: Credit
        """

        if not hasattr(self, 'credits'):
            raise exc.FundingSourceNotCreditable()
        return Credit(
            href=self.credits.href,
            amount=amount,
            **kwargs
        ).save()

class BankAccount(FundingInstrument):
    """
    A BankAccount is both a source, and a destination of, funds. You may
    create Debits and Credits to and from, this funding instrument.
    """

    type = 'bank_accounts'

    uri_gen = wac.URIGen('/bank_accounts', '{bank_account}')

    def verify(self):
        """
        Creates a verification of the associated BankAccount so it can
        perform verified operations (debits).

        :rtype: BankAccountVerification
        """
        return BankAccountVerification(
            href=self.bank_account_verifications.href
        ).save()

class BankAccountVerification(Resource):
    """
    Represents an attempt to verify the associated BankAccount so it can
    perform verified operations (debits).
    """

    type = 'bank_account_verifications'

    def confirm(self, amount_1, amount_2):
        self.amount_1 = amount_1
        self.amount_2 = amount_2
        return self.save()

class Card(FundingInstrument):
    """
    A card represents a source of funds. You may Debit funds from the Card.
    """

    type = 'cards'

    uri_gen = wac.URIGen('/cards', '{card}')

    def hold(self, amount, **kwargs):
        return CardHold(
            href=self.card_holds.href,
            amount=amount,
            **kwargs
        ).save()

class Customer(Resource):
    """
    A Customer represents a business or person within your Marketplace. A
    Customer can have many funding instruments such as cards and bank accounts
    associated to them. Customers are logical grouping constructs for
    associating many Transactions and FundingInstruments.
    """

    type = 'customers'

    uri_gen = wac.URIGen('/customers', '{customer}')

    def create_order(self, **kwargs):
        return Order(href=self.orders.href, **kwargs).save()

class Order(Resource):
    """
    An Order is a logical construct for grouping Transactions.

    An Order may have 0:n Transactions associated with it so long as the sum
    (`amount_escrowed`) which is calculated as
    (Debits - Refunds - Credits + Reversals), is always >= 0.
    """

    type = 'orders'

    uri_gen = wac.URIGen('/orders', '{order}')

    def credit_to(self, destination, amount, **kwargs):
        return destination.credit(order=self.href,
                                  amount=amount,
                                  **kwargs)

    def debit_from(self, source, amount, **kwargs):
        return source.debit(order=self.href,
                            amount=amount,
                            **kwargs)

class Callback(Resource):
    """
    A Callback is a publicly accessible location that can receive POSTed JSON
    data whenever an Event is generated.
    """

    type = 'callbacks'

    uri_gen = wac.URIGen('/callbacks', '{callback}')

class Dispute(Resource):
    """
    A dispute occurs when a customer disputes a transaction that
    occurred on their funding instrument.
    """
    type = 'disputes'

    uri_gen = wac.URIGen('/disputes', '{dispute}')

class Event(Resource):
    """
    An Event is a snapshot of another resource at a point in time when
    something significant occurred. Events are created when resources are
    created, updated, deleted or otherwise change state such as a Credit being
    marked as failed.
    """

    type = 'events'

    uri_gen = wac.URIGen('/events', '{event}')

class EventCallback(Resource):
    """
    Represents a single event being sent to a callback.
    """

    type = 'event_callbacks'

class EventCallbackLog(Resource):
    """
    Represents a request and response from single attempt to notify a callback
    of an event.
    """

    type = 'event_callback_logs'

class ExternalAccount(FundingInstrument):
    """
    An External Account represents a source of funds provided by an external, 3rd
    party processor. You may Debit funds from the account if can_debit is true.
    """

    type = 'external_accounts'

    uri_gen = wac.URIGen('/external_accounts', '{external_account}')
<|endoftext|>


<commit_before># This file is part of QuTiP: Quantum Toolbox in Python.
#
#    Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
#    All rights reserved.
#
#    Redistribution and use in source and binary forms, with or without
#    modification, are permitted provided that the following conditions are
#    met:
#
#    1. Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#
#    2. Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#
#    3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names
#       of its contributors may be used to endorse or promote products derived
#       from this software without specific prior written permission.
#
#    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
#    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
###############################################################################

import numpy as np
from numpy.testing import assert_, run_module_suite

from qutip.operators import (num, destroy,
                             sigmax, sigmay, sigmaz, sigmam, sigmap)
from qutip.states import fock, fock_dm
from qutip.expect import expect
from qutip.mesolve import mesolve

class TestExpect:
    """
    A test class for the QuTiP function for calculating expectation values.
    """

    def testOperatorKet(self):
        """
        expect: operator and ket
        """
        N = 10
        op_N = num(N)
        op_a = destroy(N)
        for n in range(N):
            e = expect(op_N, fock(N, n))
            assert_(e == n)
            assert_(type(e) == float)
            e = expect(op_a, fock(N, n))
            assert_(e == 0)
            assert_(type(e) == complex)

    def testOperatorDensityMatrix(self):
        """
        expect: operator and density matrix
        """
        N = 10
        op_N = num(N)
        op_a = destroy(N)
        for n in range(N):
            e = expect(op_N, fock_dm(N, n))
            assert_(e == n)
            assert_(type(e) == float)
            e = expect(op_a, fock_dm(N, n))
            assert_(e == 0)
            assert_(type(e) == complex)

    def testOperatorStateList(self):
        """
        expect: operator and state list
        """
        N = 10
        op = num(N)

        res = expect(op, [fock(N, n) for n in range(N)])
        assert_(all(res == range(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.float64)

        res = expect(op, [fock_dm(N, n) for n in range(N)])
        assert_(all(res == range(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.float64)

        op = destroy(N)

        res = expect(op, [fock(N, n) for n in range(N)])
        assert_(all(res == np.zeros(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.complex128)

        res = expect(op, [fock_dm(N, n) for n in range(N)])
        assert_(all(res == np.zeros(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.complex128)

    def testOperatorListState(self):
        """
        expect: operator list and state
        """
        res = expect([sigmax(), sigmay(), sigmaz()], fock(2, 0))
        assert_(len(res) == 3)
        assert_(all(abs(res - [0, 0, 1]) < 1e-12))

        res = expect([sigmax(), sigmay(), sigmaz()], fock_dm(2, 1))
        assert_(len(res) == 3)
        assert_(all(abs(res - [0, 0, -1]) < 1e-12))

    def testOperatorListStateList(self):
        """
        expect: operator list and state list
        """
        operators = [sigmax(), sigmay(), sigmaz(), sigmam(), sigmap()]
        states = [fock(2, 0), fock(2, 1), fock_dm(2, 0), fock_dm(2, 1)]
        res = expect(operators, states)

        assert_(len(res) == len(operators))

        for r_idx, r in enumerate(res):

            assert_(isinstance(r, np.ndarray))

            if operators[r_idx].isherm:
                assert_(r.dtype == np.float64)
            else:
                assert_(r.dtype == np.complex128)

            for s_idx, s in enumerate(states):
                assert_(r[s_idx] == expect(operators[r_idx], states[s_idx]))

    def testExpectSolverCompatibility(self):
        """
        expect: operator list and state list
        """
        c_ops = [0.0001 * sigmaz()]
        e_ops = [sigmax(), sigmay(), sigmaz(), sigmam(), sigmap()]
        times = np.linspace(0, 10, 100)

        res1 = mesolve(sigmax(), fock(2, 0), times, c_ops, e_ops)
        res2 = mesolve(sigmax(), fock(2, 0), times, c_ops, [])

        e1 = res1.expect
        e2 = expect(e_ops, res2.states)

        assert_(len(e1) == len(e2))

        for n in range(len(e1)):
            assert_(len(e1[n]) == len(e2[n]))
            assert_(isinstance(e1[n], np.ndarray))
            assert_(isinstance(e2[n], np.ndarray))
            assert_(e1[n].dtype == e2[n].dtype)
            assert_(all(abs(e1[n] - e2[n]) < 1e-12))

if __name__ == "__main__":
    run_module_suite()
<commit_msg>add randomized expect tests<commit_after># This file is part of QuTiP: Quantum Toolbox in Python.
#
#    Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
#    All rights reserved.
#
#    Redistribution and use in source and binary forms, with or without
#    modification, are permitted provided that the following conditions are
#    met:
#
#    1. Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#
#    2. Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#
#    3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names
#       of its contributors may be used to endorse or promote products derived
#       from this software without specific prior written permission.
#
#    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
#    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
###############################################################################

import numpy as np
from numpy.testing import assert_, run_module_suite

from qutip.operators import (num, destroy,
                             sigmax, sigmay, sigmaz, sigmam, sigmap)
from qutip.states import fock, fock_dm
from qutip.expect import expect
from qutip.mesolve import mesolve
from qutip.random_objects import rand_herm, rand_ket

class TestExpect:
    """
    A test class for the QuTiP function for calculating expectation values.
    """

    def testOperatorKet(self):
        """
        expect: operator and ket
        """
        N = 10
        op_N = num(N)
        op_a = destroy(N)
        for n in range(N):
            e = expect(op_N, fock(N, n))
            assert_(e == n)
            assert_(type(e) == float)
            e = expect(op_a, fock(N, n))
            assert_(e == 0)
            assert_(type(e) == complex)

    def testOperatorKetRand(self):
        """
        expect: rand operator & rand ket
        """
        for kk in range(20):
            N = 20
            H = rand_herm(N, 0.2)
            psi = rand_ket(N,0.3)
            out = expect(H,psi)
            ans = (psi.dag()*H*psi).tr()
            assert_(np.abs(out-ans) < 1e-14)

            G = rand_herm(N, 0.1)
            out = expect(H+1j*G,psi)
            ans = (psi.dag()*(H+1j*G)*psi).tr()
            assert_(np.abs(out-ans) < 1e-14)

    def testOperatorDensityMatrix(self):
        """
        expect: operator and density matrix
        """
        N = 10
        op_N = num(N)
        op_a = destroy(N)
        for n in range(N):
            e = expect(op_N, fock_dm(N, n))
            assert_(e == n)
            assert_(type(e) == float)
            e = expect(op_a, fock_dm(N, n))
            assert_(e == 0)
            assert_(type(e) == complex)

    def testOperatorStateList(self):
        """
        expect: operator and state list
        """
        N = 10
        op = num(N)

        res = expect(op, [fock(N, n) for n in range(N)])
        assert_(all(res == range(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.float64)

        res = expect(op, [fock_dm(N, n) for n in range(N)])
        assert_(all(res == range(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.float64)

        op = destroy(N)

        res = expect(op, [fock(N, n) for n in range(N)])
        assert_(all(res == np.zeros(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.complex128)

        res = expect(op, [fock_dm(N, n) for n in range(N)])
        assert_(all(res == np.zeros(N)))
        assert_(isinstance(res, np.ndarray) and res.dtype == np.complex128)

    def testOperatorListState(self):
        """
        expect: operator list and state
        """
        res = expect([sigmax(), sigmay(), sigmaz()], fock(2, 0))
        assert_(len(res) == 3)
        assert_(all(abs(res - [0, 0, 1]) < 1e-12))

        res = expect([sigmax(), sigmay(), sigmaz()], fock_dm(2, 1))
        assert_(len(res) == 3)
        assert_(all(abs(res - [0, 0, -1]) < 1e-12))

    def testOperatorListStateList(self):
        """
        expect: operator list and state list
        """
        operators = [sigmax(), sigmay(), sigmaz(), sigmam(), sigmap()]
        states = [fock(2, 0), fock(2, 1), fock_dm(2, 0), fock_dm(2, 1)]
        res = expect(operators, states)

        assert_(len(res) == len(operators))

        for r_idx, r in enumerate(res):

            assert_(isinstance(r, np.ndarray))

            if operators[r_idx].isherm:
                assert_(r.dtype == np.float64)
            else:
                assert_(r.dtype == np.complex128)

            for s_idx, s in enumerate(states):
                assert_(r[s_idx] == expect(operators[r_idx], states[s_idx]))

    def testExpectSolverCompatibility(self):
        """
        expect: operator list and state list
        """
        c_ops = [0.0001 * sigmaz()]
        e_ops = [sigmax(), sigmay(), sigmaz(), sigmam(), sigmap()]
        times = np.linspace(0, 10, 100)

        res1 = mesolve(sigmax(), fock(2, 0), times, c_ops, e_ops)
        res2 = mesolve(sigmax(), fock(2, 0), times, c_ops, [])

        e1 = res1.expect
        e2 = expect(e_ops, res2.states)

        assert_(len(e1) == len(e2))

        for n in range(len(e1)):
            assert_(len(e1[n]) == len(e2[n]))
            assert_(isinstance(e1[n], np.ndarray))
            assert_(isinstance(e2[n], np.ndarray))
            assert_(e1[n].dtype == e2[n].dtype)
            assert_(all(abs(e1[n] - e2[n]) < 1e-12))

if __name__ == "__main__":
    run_module_suite()
<|endoftext|>


<commit_before>import socket
try:
    from select import poll, POLLIN
except ImportError:  # `poll` doesn't exist on OSX and other platforms
    poll = False
    try:
        from select import select
    except ImportError:  # `select` doesn't exist on AppEngine.
        select = False

def is_connection_dropped(conn):  # Platform-specific
    """
    Returns True if the connection is dropped and should be closed.

    :param conn:
        :class:`httplib.HTTPConnection` object.

    Note: For platforms like AppEngine, this will always return ``False`` to
    let the platform handle connection recycling transparently for us.
    """
    sock = getattr(conn, 'sock', False)
    if sock is False:  # Platform-specific: AppEngine
        return False
    if sock is None:  # Connection already closed (such as by httplib).
        return True

    if not poll:
        if not select:  # Platform-specific: AppEngine
            return False

        try:
            return select([sock], [], [], 0.0)[0]
        except socket.error:
            return True

    # This version is better on platforms that support it.
    p = poll()
    p.register(sock, POLLIN)
    for (fno, ev) in p.poll(0.0):
        if fno == sock.fileno():
            # Either data is buffered (bad), or the connection is dropped.
            return True

# This function is copied from socket.py in the Python 2.7 standard
# library test suite. Added to its signature is only `socket_options`.
def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                      source_address=None, socket_options=None):
    """Connect to *address* and return the socket object.

    Convenience function.  Connect to *address* (a 2-tuple ``(host,
    port)``) and return the socket object.  Passing the optional
    *timeout* parameter will set the timeout on the socket instance
    before attempting to connect.  If no *timeout* is supplied, the
    global default timeout setting returned by :func:`getdefaulttimeout`
    is used.  If *source_address* is set it must be a tuple of (host, port)
    for the socket to bind as a source address before making the connection.
    An host of '' or port 0 tells the OS to use the default.
    """

    host, port = address
    err = None
    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
        af, socktype, proto, canonname, sa = res
        sock = None
        try:
            sock = socket.socket(af, socktype, proto)
            if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
                sock.settimeout(timeout)
            if source_address:
                sock.bind(source_address)
            # If provided, set socket level options before connecting
            _set_socket_options(sock, socket_options)
            sock.connect(sa)
            return sock

        except socket.error as _:
            err = _
            if sock is not None:
                sock.close()

    if err is not None:
        raise err
    else:
        raise socket.error("getaddrinfo returns an empty list")

def _set_socket_options(sock, options):
    if options is None:
        return

    for opt in options:
        sock.setsocketopt(*opt)
<commit_msg>Add comment and fix usage of setsockopt<commit_after>import socket
try:
    from select import poll, POLLIN
except ImportError:  # `poll` doesn't exist on OSX and other platforms
    poll = False
    try:
        from select import select
    except ImportError:  # `select` doesn't exist on AppEngine.
        select = False

def is_connection_dropped(conn):  # Platform-specific
    """
    Returns True if the connection is dropped and should be closed.

    :param conn:
        :class:`httplib.HTTPConnection` object.

    Note: For platforms like AppEngine, this will always return ``False`` to
    let the platform handle connection recycling transparently for us.
    """
    sock = getattr(conn, 'sock', False)
    if sock is False:  # Platform-specific: AppEngine
        return False
    if sock is None:  # Connection already closed (such as by httplib).
        return True

    if not poll:
        if not select:  # Platform-specific: AppEngine
            return False

        try:
            return select([sock], [], [], 0.0)[0]
        except socket.error:
            return True

    # This version is better on platforms that support it.
    p = poll()
    p.register(sock, POLLIN)
    for (fno, ev) in p.poll(0.0):
        if fno == sock.fileno():
            # Either data is buffered (bad), or the connection is dropped.
            return True

# This function is copied from socket.py in the Python 2.7 standard
# library test suite. Added to its signature is only `socket_options`.
def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                      source_address=None, socket_options=None):
    """Connect to *address* and return the socket object.

    Convenience function.  Connect to *address* (a 2-tuple ``(host,
    port)``) and return the socket object.  Passing the optional
    *timeout* parameter will set the timeout on the socket instance
    before attempting to connect.  If no *timeout* is supplied, the
    global default timeout setting returned by :func:`getdefaulttimeout`
    is used.  If *source_address* is set it must be a tuple of (host, port)
    for the socket to bind as a source address before making the connection.
    An host of '' or port 0 tells the OS to use the default.
    """

    host, port = address
    err = None
    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
        af, socktype, proto, canonname, sa = res
        sock = None
        try:
            sock = socket.socket(af, socktype, proto)
            if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
                sock.settimeout(timeout)
            if source_address:
                sock.bind(source_address)
            # If provided, set socket level options before connecting.
            # This is the only addition urllib3 makes to this function.
            _set_socket_options(sock, socket_options)
            sock.connect(sa)
            return sock

        except socket.error as _:
            err = _
            if sock is not None:
                sock.close()

    if err is not None:
        raise err
    else:
        raise socket.error("getaddrinfo returns an empty list")

def _set_socket_options(sock, options):
    if options is None:
        return

    for opt in options:
        sock.setsockopt(*opt)
<|endoftext|>


<commit_before>"""Assert requirements.txt file and actual installed dependencies match.

* Use the ``pip freeze`` command to determine what are the installed libraries,
  and find differences from the written requirements.
* At the end, write the actual dependencies to the file.
"""
from subprocess import check_call, check_output

GET_DEPENDENCIES_COMMAND = ["pip", "freeze", "--exclude-editable"]

def main():
    with open("requirements.txt", "rt") as requirements_file:
        written_requirements = dict(
            line.split("==")
            for line in requirements_file.read().split())

    actual_requirements = dict(
        line.split("==")
        for line in check_output(
            GET_DEPENDENCIES_COMMAND).decode("utf-8").split())

    try:
        diff = "Difference: {}".format(
            ", ".join(set(actual_requirements) ^ set(written_requirements)))
        assert actual_requirements == written_requirements, diff

    finally:
        with open("requirements.txt", "wt") as requirements_file:
            check_call(GET_DEPENDENCIES_COMMAND, stdout=requirements_file)

if __name__ == "__main__":
    main()
<commit_msg>fixed diff output of requirements validation<commit_after>"""Assert requirements.txt file and actual installed dependencies match.

* Use the ``pip freeze`` command to determine what are the installed libraries,
  and find differences from the written requirements.
* At the end, write the actual dependencies to the file.
"""
from subprocess import check_call, check_output

GET_DEPENDENCIES_COMMAND = ["pip", "freeze", "--exclude-editable"]

def main():
    with open("requirements.txt", "rt") as requirements_file:
        written_requirements = set(requirements_file.read().split())

    actual_requirements = set(check_output(
        GET_DEPENDENCIES_COMMAND).decode("utf-8").split())

    try:
        diff = "Difference: {}".format(
            ", ".join(actual_requirements ^ written_requirements))
        assert actual_requirements == written_requirements, diff

    finally:
        with open("requirements.txt", "wt") as requirements_file:
            check_call(GET_DEPENDENCIES_COMMAND, stdout=requirements_file)

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import time
from warnings import warn

import numpy as np

def iteratePairs(iterable):
    last = None
    for idx in range(len(iterable) - 1):
        nxt = iterable.next()
        if last is not None:
            yield last, nxt
        last = nxt

class GerberWriter:
    """
    Class for the output of gerber vector graphics files.
    This can be used to create layers of a PCB or similar
    project.  It takes in a filepath, or file-like object.
    It takes care of several tediuous error-prone steps namely:
      1. Most supported gerber graphics primitives. (Aperature Macros coming)
      2. Aperature generation and unification
      3. Layer/polarity settings
      4. Formatting + precision/underflow concerns.
      5. Some machine/legacy specific considerations of format
    """
    def __init__(self, pathOrFlo, fFmt=(4, 6), units="MM", comment=None):
        if hasattr(pathOrFlo, "write"):
            self._f = pathOrFlo
        else:
            self._f = open(pathOrFlo, "w")

        self._polaritySet = False
        self._inPolygonMode = False
        self._aprDict = {}

        self._currentX = 0
        self._currentY = 0

        # Sanity check unit formats, and store
        assert len(fFmt) == 2
        self._fFmt = fFmt

        # Sanity check the header bits
        assert units in ["MM", "IN"], "Units much be IN or MM"
        self._units = units

        # Write the gerber file headers
        if comment is None:
            comment = "Autogenerated by babyfood by MRG on " + time.ctime()
        self.writeComment(comment)

        fmtStr = "X%i%iY%i%i" % (self._fFmt + self._fFmt)
        self._f.write("%FSLA" + fmtStr + "*%\n")
        self._f.write("%MO" + units + "*%\n")

        self._currentAperatureCode = None
        self.setLayerPolarity()

    def _trimFloatToPrecision(self, flt, radix=""):
        fmtStr = "%.0" + str(self._fFmt[1]) + "f"
        formatted = fmtStr % round(float(flt), self._fFmt[1])
        beforeDecimal, afterDecimal = formatted.split(".")

        # Warn about MSB clipping. This should probably raise an exception?
        if len(beforeDecimal) > self._fFmt[0]:
            wStr = "WARNING: position %f is getting truncated to %s.%s (msb)!"
            warn(wStr % (flt, beforeDecimal, afterDecimal))
            beforeDecimal = beforeDecimal[0:self._fFmt[0]]

        return beforeDecimal + radix + afterDecimal

    def _fmtCoord(self, xLoc, yLoc, xLab="X", yLab="Y"):
        xStr = xLab + self._trimFloatToPrecision(xLoc)
        yStr = yLab + self._trimFloatToPrecision(yLoc)
        return xStr + yStr

    def setLayerPolarity(self, polarity="D"):
        '''
        Set the layer polarity.  This can be used to toggle between:
        (D)ark - inking of trace/mask
        (C)lear - Removing where mask previously was.
        '''
        assert polarity in ["D", "C"]
        self._f.write("%LP" + polarity + "*%\n")

    def writeComment(self, comment):
        assert "*" not in comment, "* character not allowed in comments"
        self._f.write("G04 " + comment + "*\n")

    def _checkAperature(self):
        # Aperatures not needed for polygon mode, so punt this check
        if self._inPolygonMode:
            return

        if not self._currentAperatureCode:
            raise RuntimeError("No Aperature defined for current move!")

    def _preGraphicsCheck(self):
        self._forceHeader()
        self._forcePolarity()

    def _writeAperatureToFile(self, aprString):
        # Format the aperature to the next available D-code
        newAprCode = "D%i" % (10 + len(self._aprDict))

        # Write it to the file, and add it to the index
        self._f.write("%AD" + newAprCode + aprString + "*%\n")
        self._aprDict[aprString] = newAprCode

        # Check the count, and warn when we pass the large mark
        if len(self._aprDict) > 999:
            warn("WARNING! Aperature counts above 999 not supported by all machines.")

    def _defineAperature(self, aprString, setAsCurrent):
        # See if we already have this aperature
        # If not, add it to the aperature registry
        if aprString not in self._aprDict:
            self._writeAperatureToFile(aprString)

        # Look up the aperature
        thisAprCode = self._aprDict[aprString]

        # Set it to current if requested
        if setAsCurrent:
            self.setAperature(thisAprCode)

        # Return the code of possible reuse
        return thisAprCode

    def setAperature(self, aprCode):
        assert aprCode in self._aprDict.values(), aprCode + " is not in " + str(self._aprDict)
        self._f.write(aprCode + "*\n")
        self._currentAperatureCode = aprCode

    def finalize(self):
        self._f.write("M02*\n")
        self._f.flush()
        self._f.close()

    def _linearMove(self, endX, endY, dCode):
        '''
        Low level command which encompasses all linear moves,
        This includes light, dark, and flash moves
        '''
        # Make sure an aperature is defined.
        self._checkAperature()

        formattedCoords = self._fmtCoord(endX, endY)
        dCodeStr = "D%02i" % dCode
        # Stroke w/ current aperature
        if dCode == 1:
            cmdString = "G01" + formattedCoords + dCodeStr
        # Light off move and flash
        elif dCode in [2, 3]:
            cmdString = formattedCoords + dCodeStr
        else:
            raise RuntimeError("Unrecognized dCode " + dCodeStr)
        self._f.write(cmdString + "*\n")

        self._currentX = endX
        self._currentY = endY

    def _arcMove(self, endX, endY, cX, cY, direction, dCode=1):
        # Make sure an aperature is defined.
        self._checkAperature()

        # Make estiamates of the radius, and sanity check the coords
        rEst1 = np.sqrt((self._currentX - cX) ** 2 + (self._currentY - cY) ** 2)
        rEst2 = np.sqrt((endX - cX) ** 2 + (endY - cY) ** 2)
        if np.abs(rEst1 - rEst2) > 0.001:
            warn("WARNING: Large deviation in computed radius in arc-move!")

        dCode = "D%02i" % dCode

        if direction == "CW":
            gCode = "G02"
        elif direction == "CCW":
            gCode = "G03"
        else:
            raise RuntimeError("Direction must be CW or CCW!")

        # Compute the offset of the circle center point from the starting coordiante
        xOffset = cX - self._currentX
        yOffset = cY - self._currentY

        # Assemble the command string
        cmdString = gCode
        cmdString += self._fmtCoord(endX, endY)
        cmdString += self._fmtCoord(xOffset, yOffset, xLab="I", yLab="J")
        cmdString += dCode

        # Set to multi-quadrant mode
        self._f.write("G75*\n")

        # Write it
        self._f.write(cmdString + "*\n")

        # Update the internal position tracker
        self._currentX = endX
        self._currentY = endY

    def _startPolygonMode(self):
        self._f.write("G36*\n")
        self._inPolygonMode = True

    def _stopPolygonMode(self, *args):
        # MRG TODO: Check args . . .
        self._f.write("G37*\n")
        self._inPolygonMode = False
<commit_msg>D-code correction<commit_after>import time
from warnings import warn

import numpy as np

def iteratePairs(iterable):
    last = None
    for idx in range(len(iterable) - 1):
        nxt = iterable.next()
        if last is not None:
            yield last, nxt
        last = nxt

class GerberWriter:
    """
    Class for the output of gerber vector graphics files.
    This can be used to create layers of a PCB or similar
    project.  It takes in a filepath, or file-like object.
    It takes care of several tediuous error-prone steps namely:
      1. Most supported gerber graphics primitives. (Aperature Macros coming)
      2. Aperature generation and unification
      3. Layer/polarity settings
      4. Formatting + precision/underflow concerns.
      5. Some machine/legacy specific considerations of format
    """
    def __init__(self, pathOrFlo, fFmt=(4, 6), units="MM", comment=None):
        if hasattr(pathOrFlo, "write"):
            self._f = pathOrFlo
        else:
            self._f = open(pathOrFlo, "w")

        self._polaritySet = False
        self._inPolygonMode = False
        self._aprDict = {}

        self._currentX = 0
        self._currentY = 0

        # Sanity check unit formats, and store
        assert len(fFmt) == 2
        self._fFmt = fFmt

        # Sanity check the header bits
        assert units in ["MM", "IN"], "Units much be IN or MM"
        self._units = units

        # Write the gerber file headers
        if comment is None:
            comment = "Autogenerated by babyfood by MRG on " + time.ctime()
        self.writeComment(comment)

        fmtStr = "X%i%iY%i%i" % (self._fFmt + self._fFmt)
        self._f.write("%FSLA" + fmtStr + "*%\n")
        self._f.write("%MO" + units + "*%\n")

        self._currentAperatureCode = None
        self.setLayerPolarity()

    def _trimFloatToPrecision(self, flt, radix=""):
        fmtStr = "%.0" + str(self._fFmt[1]) + "f"
        formatted = fmtStr % round(float(flt), self._fFmt[1])
        beforeDecimal, afterDecimal = formatted.split(".")

        # Warn about MSB clipping. This should probably raise an exception?
        if len(beforeDecimal) > self._fFmt[0]:
            wStr = "WARNING: position %f is getting truncated to %s.%s (msb)!"
            warn(wStr % (flt, beforeDecimal, afterDecimal))
            beforeDecimal = beforeDecimal[0:self._fFmt[0]]

        return beforeDecimal + radix + afterDecimal

    def _fmtCoord(self, xLoc, yLoc, xLab="X", yLab="Y"):
        xStr = xLab + self._trimFloatToPrecision(xLoc)
        yStr = yLab + self._trimFloatToPrecision(yLoc)
        return xStr + yStr

    def setLayerPolarity(self, polarity="D"):
        '''
        Set the layer polarity.  This can be used to toggle between:
        (D)ark - inking of trace/mask
        (C)lear - Removing where mask previously was.
        '''
        assert polarity in ["D", "C"]
        self._f.write("%LP" + polarity + "*%\n")

    def writeComment(self, comment):
        assert "*" not in comment, "* character not allowed in comments"
        self._f.write("G04 " + comment + "*\n")

    def _checkAperature(self):
        # Aperatures not needed for polygon mode, so punt this check
        if self._inPolygonMode:
            return

        if not self._currentAperatureCode:
            raise RuntimeError("No Aperature defined for current move!")

    def _preGraphicsCheck(self):
        self._forceHeader()
        self._forcePolarity()

    def _writeAperatureToFile(self, aprString):
        # Format the aperature to the next available D-code
        dCodeNumber = 10 + len(self._aprDict)
        newAprCode = "D%i" % dCodeNumber

        # Write it to the file, and add it to the index
        self._f.write("%AD" + newAprCode + aprString + "*%\n")
        self._aprDict[aprString] = newAprCode

        # Check the count, and warn when we pass the large mark
        if len(dCodeNumber) > 999:
            warn("WARNING! Aperature counts above 999 not supported by all machines.")

    def _defineAperature(self, aprString, setAsCurrent):
        # See if we already have this aperature
        # If not, add it to the aperature registry
        if aprString not in self._aprDict:
            self._writeAperatureToFile(aprString)

        # Look up the aperature
        thisAprCode = self._aprDict[aprString]

        # Set it to current if requested
        if setAsCurrent:
            self.setAperature(thisAprCode)

        # Return the code of possible reuse
        return thisAprCode

    def setAperature(self, aprCode):
        assert aprCode in self._aprDict.values(), aprCode + " is not in " + str(self._aprDict)
        self._f.write(aprCode + "*\n")
        self._currentAperatureCode = aprCode

    def finalize(self):
        self._f.write("M02*\n")
        self._f.flush()
        self._f.close()

    def _linearMove(self, endX, endY, dCode):
        '''
        Low level command which encompasses all linear moves,
        This includes light, dark, and flash moves
        '''
        # Make sure an aperature is defined.
        self._checkAperature()

        formattedCoords = self._fmtCoord(endX, endY)
        dCodeStr = "D%02i" % dCode
        # Stroke w/ current aperature
        if dCode == 1:
            cmdString = "G01" + formattedCoords + dCodeStr
        # Light off move and flash
        elif dCode in [2, 3]:
            cmdString = formattedCoords + dCodeStr
        else:
            raise RuntimeError("Unrecognized dCode " + dCodeStr)
        self._f.write(cmdString + "*\n")

        self._currentX = endX
        self._currentY = endY

    def _arcMove(self, endX, endY, cX, cY, direction, dCode=1):
        # Make sure an aperature is defined.
        self._checkAperature()

        # Make estiamates of the radius, and sanity check the coords
        rEst1 = np.sqrt((self._currentX - cX) ** 2 + (self._currentY - cY) ** 2)
        rEst2 = np.sqrt((endX - cX) ** 2 + (endY - cY) ** 2)
        if np.abs(rEst1 - rEst2) > 0.001:
            warn("WARNING: Large deviation in computed radius in arc-move!")

        dCode = "D%02i" % dCode

        if direction == "CW":
            gCode = "G02"
        elif direction == "CCW":
            gCode = "G03"
        else:
            raise RuntimeError("Direction must be CW or CCW!")

        # Compute the offset of the circle center point from the starting coordiante
        xOffset = cX - self._currentX
        yOffset = cY - self._currentY

        # Assemble the command string
        cmdString = gCode
        cmdString += self._fmtCoord(endX, endY)
        cmdString += self._fmtCoord(xOffset, yOffset, xLab="I", yLab="J")
        cmdString += dCode

        # Set to multi-quadrant mode
        self._f.write("G75*\n")

        # Write it
        self._f.write(cmdString + "*\n")

        # Update the internal position tracker
        self._currentX = endX
        self._currentY = endY

    def _startPolygonMode(self):
        self._f.write("G36*\n")
        self._inPolygonMode = True

    def _stopPolygonMode(self, *args):
        # MRG TODO: Check args . . .
        self._f.write("G37*\n")
        self._inPolygonMode = False
<|endoftext|>


<commit_before>"""
homeassistant.components.sensor.swiss_public_transport
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Swiss public transport sensor will give you the next two departure times
from a given location to another one. This sensor is limited to Switzerland.

Configuration:

To use the Swiss public transport sensor you will need to add something like
the following to your config/configuration.yaml

sensor:
  platform: swiss_public_transport
  from: STATION_ID
  to: STATION_ID

Variables:

from
*Required
Start station/stop of your trip. To search for the ID of the station, use the
an URL like this: http://transport.opendata.ch/v1/locations?query=Wankdorf
to query for the station. If the score is 100 ("score":"100" in the response),
it is a perfect match.

to
*Required
Destination station/stop of the trip. Same procedure as for the start station.

Details for the API : http://transport.opendata.ch
"""
import logging
from datetime import timedelta
from requests import get

from homeassistant.util import Throttle
import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)
_RESOURCE = 'http://transport.opendata.ch/v1/'

# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)

def setup_platform(hass, config, add_devices, discovery_info=None):
    """ Get the Swiss public transport sensor. """

    # journal contains [0] Station ID start, [1] Station ID destination
    # [2] Station name start, and [3] Station name destination
    journey = []
    journey.append(config.get('from', None))
    journey.append(config.get('to', None))
    try:
        for location in [config.get('from', None), config.get('to', None)]:
            # transport.opendata.ch doesn't play nice with requests.Session
            result = get(_RESOURCE + 'locations?query=%s' % location)
            journey.append(result.json()['stations'][0]['name'])
    except KeyError:
        _LOGGER.error(
            "Unable to determine stations. "
            "Check your settings and/or the availability of opendata.ch")

        return None

    dev = []
    data = PublicTransportData(journey)
    dev.append(SwissPublicTransportSensor(data, journey))
    add_devices(dev)

# pylint: disable=too-few-public-methods
class SwissPublicTransportSensor(Entity):
    """ Implements an Swiss public transport sensor. """

    def __init__(self, data, journey):
        self.data = data
        self._name = '{}-{}'.format(journey[2], journey[3])
        self.update()

    @property
    def name(self):
        """ Returns the name. """
        return self._name

    @property
    def state(self):
        """ Returns the state of the device. """
        return self._state

    # pylint: disable=too-many-branches
    def update(self):
        """ Gets the latest data from opendata.ch and updates the states. """
        times = self.data.update()
        if times is not None:
            self._state = ', '.join(times)

# pylint: disable=too-few-public-methods
class PublicTransportData(object):
    """ Class for handling the data retrieval.  """

    def __init__(self, journey):
        self.times = ['n/a', 'n/a']
        self.start = journey[0]
        self.destination = journey[1]

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    def update(self):
        """ Gets the latest data from opendata.ch. """

        response = get(
            _RESOURCE +
            'connections?' +
            'from=' + self.start + '&' +
            'to=' + self.destination + '&' +
            'fields[]=connections/from/departureTimestamp/&' +
            'fields[]=connections/')

        try:
            self.times.insert(0, dt_util.timestamp_to_short_time_str(
                response.json()['connections'][0]['from']
                ['departureTimestamp']))
            self.times.insert(1, dt_util.timestamp_to_short_time_str(
                response.json()['connections'][1]['from']
                ['departureTimestamp']))
            return self.times

        except KeyError:
            return self.times
<commit_msg>switch from error to execption for logger<commit_after>"""
homeassistant.components.sensor.swiss_public_transport
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Swiss public transport sensor will give you the next two departure times
from a given location to another one. This sensor is limited to Switzerland.

Configuration:

To use the Swiss public transport sensor you will need to add something like
the following to your config/configuration.yaml

sensor:
  platform: swiss_public_transport
  from: STATION_ID
  to: STATION_ID

Variables:

from
*Required
Start station/stop of your trip. To search for the ID of the station, use the
an URL like this: http://transport.opendata.ch/v1/locations?query=Wankdorf
to query for the station. If the score is 100 ("score":"100" in the response),
it is a perfect match.

to
*Required
Destination station/stop of the trip. Same procedure as for the start station.

Details for the API : http://transport.opendata.ch
"""
import logging
from datetime import timedelta
from requests import get

from homeassistant.util import Throttle
import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)
_RESOURCE = 'http://transport.opendata.ch/v1/'

# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)

def setup_platform(hass, config, add_devices, discovery_info=None):
    """ Get the Swiss public transport sensor. """

    # journal contains [0] Station ID start, [1] Station ID destination
    # [2] Station name start, and [3] Station name destination
    journey = []
    journey.append(config.get('from', None))
    journey.append(config.get('to', None))
    try:
        for location in [config.get('from', None), config.get('to', None)]:
            # transport.opendata.ch doesn't play nice with requests.Session
            result = get(_RESOURCE + 'locations?query=%s' % location)
            journey.append(result.json()['stations'][0]['name'])
    except KeyError:
        _LOGGER.exception(
            "Unable to determine stations. "
            "Check your settings and/or the availability of opendata.ch")

        return None

    dev = []
    data = PublicTransportData(journey)
    dev.append(SwissPublicTransportSensor(data, journey))
    add_devices(dev)

# pylint: disable=too-few-public-methods
class SwissPublicTransportSensor(Entity):
    """ Implements an Swiss public transport sensor. """

    def __init__(self, data, journey):
        self.data = data
        self._name = '{}-{}'.format(journey[2], journey[3])
        self.update()

    @property
    def name(self):
        """ Returns the name. """
        return self._name

    @property
    def state(self):
        """ Returns the state of the device. """
        return self._state

    # pylint: disable=too-many-branches
    def update(self):
        """ Gets the latest data from opendata.ch and updates the states. """
        times = self.data.update()
        if times is not None:
            self._state = ', '.join(times)

# pylint: disable=too-few-public-methods
class PublicTransportData(object):
    """ Class for handling the data retrieval.  """

    def __init__(self, journey):
        self.times = ['n/a', 'n/a']
        self.start = journey[0]
        self.destination = journey[1]

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    def update(self):
        """ Gets the latest data from opendata.ch. """

        response = get(
            _RESOURCE +
            'connections?' +
            'from=' + self.start + '&' +
            'to=' + self.destination + '&' +
            'fields[]=connections/from/departureTimestamp/&' +
            'fields[]=connections/')

        try:
            self.times.insert(0, dt_util.timestamp_to_short_time_str(
                response.json()['connections'][0]['from']
                ['departureTimestamp']))
            self.times.insert(1, dt_util.timestamp_to_short_time_str(
                response.json()['connections'][1]['from']
                ['departureTimestamp']))
            return self.times

        except KeyError:
            return self.times
<|endoftext|>


<commit_before>from cookielib import Cookie, parse_ns_headers
from json import loads
from pyramid.renderers import render
from pyramid.security import remember
from pyramid.testing import DummyRequest
from pyramid.testing import setUp, tearDown
from pytest import fixture
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError
from transaction import abort
from urllib import unquote
from webtest import TestApp as TestAppBase

def as_dict(content, **kw):
    return dict(loads(render('json', content, DummyRequest())), **kw)

def route_url(name, **kwargs):
    return unquote(DummyRequest().route_url(name, **kwargs))

def auth_cookies(login):
    environ = dict(HTTP_HOST='example.com')
    cookies = [c for _, c in remember(DummyRequest(environ=environ), login)]
    for data in parse_ns_headers(cookies):
        name, value = data.pop(0)
        rest = dict(port='80', port_specified=True, domain_specified=True,
            path_specified=True, secure=False, expires=None, discard=False,
            comment='', comment_url=None, rest=None, domain='')
        rest.update(dict(data))
        rest['domain_initial_dot'] = rest['domain'].startswith('.')
        yield Cookie(name=name, value=value, **rest)

# settings for test configuration
settings = {
    'auth.secret': 's3crit',
    'pyramid.includes': 'pyramid_mailer.testing',
    'testing': True,
}

@fixture
def config(request, tmpdir):
    """ Sets up a Pyramid `Configurator` instance suitable for testing. """
    config = setUp(settings=dict(settings, filesafe=str(tmpdir)))
    request.addfinalizer(tearDown)
    return config

@fixture(scope='session')
def connection(request):
    """ Sets up an SQLAlchemy engine and returns a connection
        to the database.  The connection string used can be overriden
        via the `PGDATABASE` environment variable. """
    from .models import db_session, metadata
    from .utils import create_db_engine
    engine = create_db_engine(suffix='_test', **settings)
    try:
        connection = engine.connect()
    except OperationalError:
        # try to create the database...
        db_url = str(engine.url).replace(engine.url.database, 'template1')
        e = create_engine(db_url)
        c = e.connect()
        c.connection.connection.set_isolation_level(0)
        c.execute('create database %s' % engine.url.database)
        c.connection.connection.set_isolation_level(1)
        c.close()
        # ...and connect again
        connection = engine.connect()
    db_session.registry.clear()
    db_session.configure(bind=connection)
    metadata.bind = engine
    metadata.drop_all(connection.engine)
    metadata.create_all(connection.engine)
    return connection

@fixture
def db_session(config, connection, request):
    """ Returns a database session object and sets up a transaction
        savepoint, which will be rolled back after running a test. """
    trans = connection.begin()          # begin a non-orm transaction
    request.addfinalizer(trans.rollback)
    request.addfinalizer(abort)
    from .models import db_session
    return db_session()

def setup_app(config):
    # TODO: can we use the given `Configurator` directly?
    from . import configure
    return configure({}, **config.registry.settings).make_wsgi_app()

class TestApp(TestAppBase):

    def get_json(self, url, params=None, headers=None, *args, **kw):
        if headers is None:
            headers = {}
        headers['Accept'] = 'application/json'
        return self.get(url, params, headers, *args, **kw)

@fixture
def app(config):
    """ Returns WSGI application wrapped in WebTest's testing interface. """
    return TestApp(setup_app(config))

@fixture
def browser(db_session, config, request):
    """ Returns an instance of `webtest.TestApp`.  The `user` pytest marker
        (`pytest.mark.user`) can be used to pre-authenticate the browser
        with the given login name: `@user('admin')`. """
    extra_environ = dict(HTTP_HOST='example.com')
    browser = TestApp(setup_app(config), extra_environ=extra_environ)
    if 'user' in request.keywords:
        # set auth cookie directly on the browser instance...
        name = request.keywords['user'].args[0]
        user = request.getfuncargvalue(name)    # get user from their fixture
        for cookie in auth_cookies(str(user.id)):
            browser.cookiejar.set_cookie(cookie)
    return browser
<commit_msg>use the main package's `configure` function when setting up an app instance for testing<commit_after>from cookielib import Cookie, parse_ns_headers
from json import loads
from pyramid.renderers import render
from pyramid.security import remember
from pyramid.testing import DummyRequest
from pyramid.testing import setUp, tearDown
from pytest import fixture
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError
from transaction import abort
from urllib import unquote
from webtest import TestApp as TestAppBase

def as_dict(content, **kw):
    return dict(loads(render('json', content, DummyRequest())), **kw)

def route_url(name, **kwargs):
    return unquote(DummyRequest().route_url(name, **kwargs))

def auth_cookies(login):
    environ = dict(HTTP_HOST='example.com')
    cookies = [c for _, c in remember(DummyRequest(environ=environ), login)]
    for data in parse_ns_headers(cookies):
        name, value = data.pop(0)
        rest = dict(port='80', port_specified=True, domain_specified=True,
            path_specified=True, secure=False, expires=None, discard=False,
            comment='', comment_url=None, rest=None, domain='')
        rest.update(dict(data))
        rest['domain_initial_dot'] = rest['domain'].startswith('.')
        yield Cookie(name=name, value=value, **rest)

# settings for test configuration
settings = {
    'auth.secret': 's3crit',
    'pyramid.includes': 'pyramid_mailer.testing',
    'testing': True,
}

@fixture
def config(request, tmpdir):
    """ Sets up a Pyramid `Configurator` instance suitable for testing. """
    config = setUp(settings=dict(settings, filesafe=str(tmpdir)))
    request.addfinalizer(tearDown)
    return config

@fixture(scope='session')
def connection(request):
    """ Sets up an SQLAlchemy engine and returns a connection
        to the database.  The connection string used can be overriden
        via the `PGDATABASE` environment variable. """
    from .models import db_session, metadata
    from .utils import create_db_engine
    engine = create_db_engine(suffix='_test', **settings)
    try:
        connection = engine.connect()
    except OperationalError:
        # try to create the database...
        db_url = str(engine.url).replace(engine.url.database, 'template1')
        e = create_engine(db_url)
        c = e.connect()
        c.connection.connection.set_isolation_level(0)
        c.execute('create database %s' % engine.url.database)
        c.connection.connection.set_isolation_level(1)
        c.close()
        # ...and connect again
        connection = engine.connect()
    db_session.registry.clear()
    db_session.configure(bind=connection)
    metadata.bind = engine
    metadata.drop_all(connection.engine)
    metadata.create_all(connection.engine)
    return connection

@fixture
def db_session(config, connection, request):
    """ Returns a database session object and sets up a transaction
        savepoint, which will be rolled back after running a test. """
    trans = connection.begin()          # begin a non-orm transaction
    request.addfinalizer(trans.rollback)
    request.addfinalizer(abort)
    from .models import db_session
    return db_session()

class TestApp(TestAppBase):

    def get_json(self, url, params=None, headers=None, *args, **kw):
        if headers is None:
            headers = {}
        headers['Accept'] = 'application/json'
        return self.get(url, params, headers, *args, **kw)

@fixture
def package():
    from .utils import get_distribution
    return __import__(get_distribution().project_name)

@fixture
def app(package, config):
    """ Returns WSGI application wrapped in WebTest's testing interface. """
    return package.configure({}, **config.registry.settings).make_wsgi_app()

@fixture
def browser(db_session, app, request):
    """ Returns an instance of `webtest.TestApp`.  The `user` pytest marker
        (`pytest.mark.user`) can be used to pre-authenticate the browser
        with the given login name: `@user('admin')`. """
    extra_environ = dict(HTTP_HOST='example.com')
    browser = TestApp(app, extra_environ=extra_environ)
    if 'user' in request.keywords:
        # set auth cookie directly on the browser instance...
        name = request.keywords['user'].args[0]
        user = request.getfuncargvalue(name)    # get user from their fixture
        for cookie in auth_cookies(str(user.id)):
            browser.cookiejar.set_cookie(cookie)
    return browser
<|endoftext|>


<commit_before>import logging
log = logging.getLogger(__name__)

from pyramid.config import Configurator
from pyramid.session import UnencryptedCookieSessionFactoryConfig

from sqlalchemy import engine_from_config
from sqlalchemy.exc import DBAPIError

from models import DBSession

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    config = Configurator(settings=settings)
    config.set_session_factory(UnencryptedCookieSessionFactoryConfig(settings['pyramid.secretcookie']))
    config.include(add_routes)
    config.include(add_views)
    config.include(add_events)
    return config.make_wsgi_app()

def add_routes(config):
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('uns.home', '/')

    # Set up the routes for "archive" urls. year/month/day
    config.add_route('uns.year.month.day.title', '/{year:\d{4}}/{month:\d{2}}/{day:\d{2}}/{title}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year.month.day', '/{year:\d{4}}/{month:\d{2}}/{day:\d{2}}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year.month', '/{year:\d{4}}/{month:\d{2}}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year', '/{year:\d{4}}/')

    # Set up routes for tags
    config.add_route('uns.tag', '/tag/{tagname}/')
    config.add_route('uns.tagnoname', '/tag/')
    config.add_route('uns.tags', '/tags/')

    # Backwards compatible routes. Blogofile created these. May end up keeping them.
    config.add_route('uns.home.pagenonum', '/page/')
    config.add_route('uns.home.page', '/page/{num:\d+}/')

    # Management routes
    config.add_route('uns.management', '/manage/')
    config.add_route('uns.management.posts', '/manage/posts/')

def add_views(config):
    config.add_view('usingnamespace.views.home.home', route_name='uns.home',
            renderer='chronological.mako')
    config.add_view('usingnamespace.views.home.redirhome',
            route_name='uns.home.pagenonum')

    config.add_view('usingnamespace.views.archive.article', route_name='uns.year.month.day.title', renderer='permapage.mako')
    config.add_view('usingnamespace.views.archive.ymd_list', route_name='uns.year.month.day', renderer='chronological.mako')
    config.add_view('usingnamespace.views.archive.ym_list', route_name='uns.year.month', renderer='chronological.mako')
    config.add_view('usingnamespace.views.archive.y_list', route_name='uns.year', renderer='yearly.mako')

    # Error pages
    #config.add_view('usingnamespace.views.errors.db_failed', context=DBAPIError, renderer='db_failed.mako')

    # Add a slash if the view has not been found.
    config.add_notfound_view('usingnamespace.views.errors.not_found', renderer='not_found.mako', append_slash=True)

def add_events(config):
    config.add_subscriber('usingnamespace.events.view_helpers.view_helpers',
            'pyramid.events.BeforeRender')

def route_zero_extend_month_day(request, elements, kw):
    if 'day' in kw:
        kw['day'] = "{0:02d}".format(kw['day'])
    if 'month' in kw:
        kw['month'] = "{0:02d}".format(kw['month'])

    return elements, kw
<commit_msg>Check for required configuration variables<commit_after>import logging
log = logging.getLogger(__name__)

from pyramid.config import Configurator
from pyramid.session import UnencryptedCookieSessionFactoryConfig

from sqlalchemy import engine_from_config
from sqlalchemy.exc import DBAPIError

from models import DBSession

required_settings = [
        'pyramid.secret.session',
        'pyramid.secret.auth',
        'usingnamespace.upload_path',
        ]

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    config = Configurator(settings=settings)

    do_start = True

    for _req in required_settings:
        if _req not in settings:
            log.error('{} is not set in configuration file.'.format(_req))
            do_start = False

    if do_start is False:
        log.error('Unable to start due to missing configuration')
        exit(-1)

    config.include(add_routes)
    config.include(add_views)
    config.include(add_events)
    return config.make_wsgi_app()

def add_routes(config):
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('uns.home', '/')

    # Set up the routes for "archive" urls. year/month/day
    config.add_route('uns.year.month.day.title', '/{year:\d{4}}/{month:\d{2}}/{day:\d{2}}/{title}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year.month.day', '/{year:\d{4}}/{month:\d{2}}/{day:\d{2}}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year.month', '/{year:\d{4}}/{month:\d{2}}/', pregenerator=route_zero_extend_month_day)
    config.add_route('uns.year', '/{year:\d{4}}/')

    # Set up routes for tags
    config.add_route('uns.tag', '/tag/{tagname}/')
    config.add_route('uns.tagnoname', '/tag/')
    config.add_route('uns.tags', '/tags/')

    # Backwards compatible routes. Blogofile created these. May end up keeping them.
    config.add_route('uns.home.pagenonum', '/page/')
    config.add_route('uns.home.page', '/page/{num:\d+}/')

    # Management routes
    config.add_route('uns.management', '/manage/')
    config.add_route('uns.management.posts', '/manage/posts/')

def add_views(config):
    config.add_view('usingnamespace.views.home.home', route_name='uns.home',
            renderer='chronological.mako')
    config.add_view('usingnamespace.views.home.redirhome',
            route_name='uns.home.pagenonum')

    config.add_view('usingnamespace.views.archive.article', route_name='uns.year.month.day.title', renderer='permapage.mako')
    config.add_view('usingnamespace.views.archive.ymd_list', route_name='uns.year.month.day', renderer='chronological.mako')
    config.add_view('usingnamespace.views.archive.ym_list', route_name='uns.year.month', renderer='chronological.mako')
    config.add_view('usingnamespace.views.archive.y_list', route_name='uns.year', renderer='yearly.mako')

    # Error pages
    #config.add_view('usingnamespace.views.errors.db_failed', context=DBAPIError, renderer='db_failed.mako')

    # Add a slash if the view has not been found.
    config.add_notfound_view('usingnamespace.views.errors.not_found', renderer='not_found.mako', append_slash=True)

def add_events(config):
    config.add_subscriber('usingnamespace.events.view_helpers.view_helpers',
            'pyramid.events.BeforeRender')

def route_zero_extend_month_day(request, elements, kw):
    if 'day' in kw:
        kw['day'] = "{0:02d}".format(kw['day'])
    if 'month' in kw:
        kw['month'] = "{0:02d}".format(kw['month'])

    return elements, kw
<|endoftext|>