diff --git a/.travis.yml b/.travis.yml index 83e5baa..47dd8ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: python python: - 2.6 - 2.7 + - 3.3 install: - python setup.py develop - pip install -r requirements.txt diff --git a/balanced/__init__.py b/balanced/__init__.py index c8617a2..98f178c 100644 --- a/balanced/__init__.py +++ b/balanced/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + __version__ = '1.0.1' diff --git a/balanced/config.py b/balanced/config.py index c746501..abf8680 100644 --- a/balanced/config.py +++ b/balanced/config.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from datetime import datetime import simplejson as json @@ -71,8 +71,8 @@ def _serialize(self, data): @staticmethod def _parse_deserialized(e): if isinstance(e, dict): - for k in e.iterkeys(): - if k.endswith('_at') and isinstance(e[k], basestring): + for k in e.keys(): + if k.endswith('_at') and isinstance(e[k], str): e[k] = iso8601.parse_date(e[k]) return e diff --git a/balanced/exc.py b/balanced/exc.py index 8ef9656..ee48f0a 100644 --- a/balanced/exc.py +++ b/balanced/exc.py @@ -1,6 +1,6 @@ -from __future__ import unicode_literals -import httplib + +import http.client import wac @@ -10,7 +10,7 @@ class BalancedError(Exception): def __str__(self): attrs = ', '.join([ '{0}={1}'.format(k, repr(v)) - for k, v in self.__dict__.iteritems() + for k, v in self.__dict__.items() ]) return '{0}({1})'.format(self.__class__.__name__, attrs) @@ -42,22 +42,22 @@ def __new__(meta_cls, name, bases, dikt): cls.types = [ getattr(cls, k) for k in dir(cls) - if k.isupper() and isinstance(getattr(cls, k), basestring) + if k.isupper() and isinstance(getattr(cls, k), str) ] - cls.type_to_error.update(zip(cls.types, [cls] * len(cls.types))) + cls.type_to_error.update(list(zip(cls.types, [cls] * len(cls.types)))) return cls def __init__(self, requests_ex): super(wac.Error, self).__init__(requests_ex) self.status_code = requests_ex.response.status_code data = getattr(requests_ex.response, 'data', {}) - for k, v in data.get('errors', [{}])[0].iteritems(): + for k, v in data.get('errors', [{}])[0].items(): setattr(self, k, v) @classmethod def format_message(cls, requests_ex): data = getattr(requests_ex.response, 'data', {}) - status = httplib.responses[requests_ex.response.status_code] + status = http.client.responses[requests_ex.response.status_code] error = data['errors'][0] status = error.pop('status', status) status_code = error.pop('status_code', diff --git a/balanced/resources.py b/balanced/resources.py index 9dc5db5..bec8cfe 100644 --- a/balanced/resources.py +++ b/balanced/resources.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import uritemplate import wac @@ -21,7 +21,7 @@ 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(): + for key, value in fields.items(): setattr(self, key, value) else: self._construct_from_response(**fields) @@ -31,11 +31,11 @@ def _construct_from_response(self, **payload): meta = payload.pop('meta', None) if isinstance(self, wac.Page): - for key, value in meta.iteritems(): + for key, value in meta.items(): setattr(self, key, value) # the remaining keys here are just hypermedia resources - for _type, resources in payload.iteritems(): + for _type, resources in payload.items(): # Singular resources are represented as JSON objects. However, # they are still wrapped inside an array: cls = Resource.registry[_type] @@ -48,7 +48,7 @@ def _construct_from_response(self, **payload): target = self else: target = cls() - for key, value in resource_body.iteritems(): + for key, value in resource_body.items(): if key in ('links',): continue setattr(target, key, value) @@ -66,7 +66,7 @@ def _hydrate(cls, payload): Construct links for objects """ links = payload.pop('links', {}) - for key, uri in links.iteritems(): + for key, uri in links.items(): variables = uritemplate.variables(uri) # marketplaces.card_holds collection, resource_type = key.split('.') @@ -103,7 +103,7 @@ def extract_variables_from_item(item, variables): # check if this is a collection or a singular item if any( parsed_link.endswith(value) - for value in item_variables.itervalues() + for value in item_variables.values() ): # singular if not item_property.endswith('_href'): @@ -162,7 +162,7 @@ def save(self): attrs = dict( (k, v.href if isinstance(v, Resource) else v) - for k, v in attrs.iteritems() + for k, v in attrs.items() if not isinstance(v, (cls.collection_cls)) ) @@ -178,10 +178,10 @@ def delete(self): self.client.delete(self.href) def __dir__(self): - return self.__dict__.keys() + return list(self.__dict__.keys()) def __getattr__(self, item): - if isinstance(item, basestring): + if isinstance(item, str): suffix = '_href' if suffix not in item: href = getattr(self, item + suffix, None) diff --git a/balanced/utils.py b/balanced/utils.py index cba22c1..cf78627 100644 --- a/balanced/utils.py +++ b/balanced/utils.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + class ClassPropertyDescriptor(object): @@ -33,7 +33,7 @@ def classproperty(func): def extract_href_from_object(obj): - if isinstance(obj, basestring): + if isinstance(obj, str): return obj if isinstance(obj, dict): return obj['href'] diff --git a/docs/source/conf.py b/docs/source/conf.py index 74e7aae..2900b33 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -42,8 +42,8 @@ master_doc = 'index' # General information about the project. -project = u'balanced' -copyright = u'2012, Balanced' +project = 'balanced' +copyright = '2012, Balanced' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -187,8 +187,8 @@ # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ - ('index', 'balanced.tex', u'balanced Documentation', - u'Balanced', 'manual'), + ('index', 'balanced.tex', 'balanced Documentation', + 'Balanced', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -217,8 +217,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'balanced', u'balanced Documentation', - [u'Balanced'], 1) + ('index', 'balanced', 'balanced Documentation', + ['Balanced'], 1) ] # If true, show URL addresses after external links. @@ -231,8 +231,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'balanced', u'balanced Documentation', - u'Balanced', 'balanced', 'One line description of project.', + ('index', 'balanced', 'balanced Documentation', + 'Balanced', 'balanced', 'One line description of project.', 'Miscellaneous'), ] diff --git a/examples/accounting.py b/examples/accounting.py index e4280f5..670cd56 100644 --- a/examples/accounting.py +++ b/examples/accounting.py @@ -32,16 +32,16 @@ def generate_report(args): if not os.path.exists('./cache'): os.makedirs('./cache') - print 'Downloading Debits' + print('Downloading Debits') debits = balanced.Debit.query.all() - print 'Downloading Credits' + print('Downloading Credits') credits = balanced.Credit.query.all() - print 'Downloading Refunds' + print('Downloading Refunds') refunds = balanced.Refund.query.all() - print 'Caching Transactions' + print('Caching Transactions') with open('cache/debits.obj', 'w') as f: pickle.dump(debits, f) @@ -66,10 +66,10 @@ def group(xs, head, tail): # {year: [(month, txn)]} txns_by_year = group(txns_by_period, - lambda (y, m, txn): y, - lambda (y, m, txn): (m, txn)) + lambda y_m_txn: y_m_txn[0], + lambda y_m_txn2: (y_m_txn2[1], y_m_txn2[2])) - group_by_month = lambda xs: group(xs, lambda (a, b): a, lambda (a, b): b) + group_by_month = lambda xs: group(xs, lambda a_b: a_b[0], lambda a_b1: a_b1[1]) # {year: {month: [txn]}} txns_by_year_by_month = {key: group_by_month(txns_by_year[key]) diff --git a/examples/bank_account_debits.py b/examples/bank_account_debits.py index 280fea0..f65d457 100644 --- a/examples/bank_account_debits.py +++ b/examples/bank_account_debits.py @@ -1,7 +1,7 @@ ''' Learn how to verify a bank account so you can debit with it. ''' -from __future__ import unicode_literals + import balanced @@ -24,20 +24,20 @@ def main(): customer = balanced.Customer().save() bank_account.associate_to_customer(customer) - print 'you can\'t debit until you authenticate' + print('you can\'t debit until you authenticate') try: bank_account.debit(100) except balanced.exc.HTTPError as ex: - print 'Debit failed, %s' % ex.message + print('Debit failed, %s' % ex.message) # verify verification = bank_account.verify() - print 'PROTIP: for TEST bank accounts the valid amount is always 1 and 1' + print('PROTIP: for TEST bank accounts the valid amount is always 1 and 1') try: verification.confirm(amount_1=1, amount_2=2) except balanced.exc.BankAccountVerificationFailure as ex: - print 'Authentication error , %s' % ex.message + print('Authentication error , %s' % ex.message) # reload verification = balanced.BankAccount.fetch( @@ -48,11 +48,11 @@ def main(): raise Exception('unpossible') debit = bank_account.debit(100) - print 'debited the bank account %s for %d cents' % ( + print('debited the bank account %s for %d cents' % ( debit.source.href, debit.amount - ) - print 'and there you have it' + )) + print('and there you have it') if __name__ == '__main__': diff --git a/examples/events_and_callbacks.py b/examples/events_and_callbacks.py index ee207e7..07ea22a 100644 --- a/examples/events_and_callbacks.py +++ b/examples/events_and_callbacks.py @@ -3,12 +3,12 @@ got good news for you. Run this example below to see how to get yourself some callback goodness and to understand how events work. """ -from __future__ import unicode_literals + import time import balanced -from helpers import RequestBinClient +from .helpers import RequestBinClient def init(): @@ -21,12 +21,12 @@ def main(): init() request_bin = RequestBinClient() - print 'let\'s create a callback' + print('let\'s create a callback') balanced.Callback( url=request_bin.callback_url, ).save() - print 'let\'s create a card and associate it with a new account' + print('let\'s create a card and associate it with a new account') card = balanced.Card( expiration_month='12', csc='123', @@ -34,41 +34,41 @@ def main(): expiration_year='2020', ).save() - print 'generate a debit (which implicitly creates and captures a hold)' + print('generate a debit (which implicitly creates and captures a hold)') card.debit(100) - print 'event creation is an async operation, let\'s wait until we have ' \ - 'some events!' + print('event creation is an async operation, let\'s wait until we have ' \ + 'some events!') while not balanced.Event.query.count(): - print 'Zzzz' + print('Zzzz') time.sleep(0) - print 'Woop, we got some events, let us see what there is to look at' + print('Woop, we got some events, let us see what there is to look at') for event in balanced.Event.query: - print 'this was a {0} event, it occurred at {1}, the callback has a ' \ + print('this was a {0} event, it occurred at {1}, the callback has a ' \ 'status of {2}'.format( event.type, event.occurred_at, event.callback_statuses - ) + )) - print 'you can inspect each event to see the logs' + print('you can inspect each event to see the logs') event = balanced.Event.query.first() for callback in event.callbacks: - print 'inspecting callback to {0} for event {1}'.format( + print('inspecting callback to {0} for event {1}'.format( callback.url, event.type, - ) + )) for log in callback.logs: - print 'this attempt to the callback has a status "{0}"'.format( + print('this attempt to the callback has a status "{0}"'.format( log.status - ) + )) - print 'ok, let\'s check with requestb.in to see if our callbacks fired' - print 'we received {0} callbacks, you can view them at {1}'.format( + print('ok, let\'s check with requestb.in to see if our callbacks fired') + print('we received {0} callbacks, you can view them at {1}'.format( len(request_bin.get_requests()), request_bin.view_url, - ) + )) if __name__ == '__main__': diff --git a/examples/examples.py b/examples/examples.py index 463fcd4..67a12a0 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -1,59 +1,59 @@ -from __future__ import unicode_literals + import balanced -print "create our new api key" +print("create our new api key") api_key = balanced.APIKey().save() -print "Our secret is: ", api_key.secret +print("Our secret is: ", api_key.secret) -print "configure with our secret " + api_key.secret +print("configure with our secret " + api_key.secret) balanced.configure(api_key.secret) -print "create our marketplace" +print("create our marketplace") marketplace = balanced.Marketplace().save() # what's my marketplace? if not balanced.Marketplace.my_marketplace: raise Exception("Marketplace.my_marketplace should not be nil") -print "what's my marketplace?, easy: Marketplace.my_marketplace: {0}".format( +print("what's my marketplace?, easy: Marketplace.my_marketplace: {0}".format( balanced.Marketplace.my_marketplace -) +)) -print "My marketplace's name is: {0}".format(marketplace.name) -print "Changing it to TestFooey" +print("My marketplace's name is: {0}".format(marketplace.name)) +print("Changing it to TestFooey") marketplace.name = "TestFooey" marketplace.save() -print "My marketplace name is now: {0}".format(marketplace.name) +print("My marketplace name is now: {0}".format(marketplace.name)) if marketplace.name != 'TestFooey': raise Exception("Marketplace name is NOT TestFooey!") -print "cool! let's create a new card." +print("cool! let's create a new card.") card = balanced.Card( number="5105105105105100", expiration_month="12", expiration_year="2015", ).save() -print "Our card href: " + card.href +print("Our card href: " + card.href) -print "create our **buyer** account" +print("create our **buyer** account") buyer = balanced.Customer(email="buyer@example.org", source=card).save() -print "our buyer account: " + buyer.href +print("our buyer account: " + buyer.href) -print "hold some amount of funds on the buyer, lets say 15$" +print("hold some amount of funds on the buyer, lets say 15$") the_hold = card.hold(1500) -print "ok, no more holds! lets just capture it (for the full amount)" +print("ok, no more holds! lets just capture it (for the full amount)") debit = the_hold.capture() -print "hmm, how much money do i have in escrow? should equal the debit amount" +print("hmm, how much money do i have in escrow? should equal the debit amount") marketplace = balanced.Marketplace.my_marketplace if marketplace.in_escrow != 1500: raise Exception("1500 is not in escrow! this is wrong") -print "i have {0} in escrow!".format(marketplace.in_escrow) +print("i have {0} in escrow!".format(marketplace.in_escrow)) -print "cool. now let me refund the full amount" +print("cool. now let me refund the full amount") refund = debit.refund() # the full amount! print ("ok, we have a merchant that's signing up, let's create an account for " @@ -78,26 +78,26 @@ destination=bank_account, ).save() -print "oh our buyer is interested in buying something for 130.00$" +print("oh our buyer is interested in buying something for 130.00$") another_debit = card.debit(13000, appears_on_statement_as="MARKETPLACE.COM") -print "lets credit our merchant 110.00$" +print("lets credit our merchant 110.00$") credit = bank_account.credit( 11000, description="Buyer purchased something on MARKETPLACE.COM") -print "lets assume the marketplace charges 15%, so it earned $20" +print("lets assume the marketplace charges 15%, so it earned $20") mp_credit = marketplace.owner_customer.bank_accounts.first().credit( 2000, description="Our commission from MARKETPLACE.COM") -print "ok lets invalid a card" +print("ok lets invalid a card") card.delete() assert buyer.cards.count() == 0 -print "invalidating a bank account" +print("invalidating a bank account") bank_account.delete() -print "associate a card with an exiting customer" +print("associate a card with an exiting customer") card = balanced.Card( number="5105105105105100", expiration_month="12", @@ -108,4 +108,4 @@ assert buyer.cards.count() == 1 -print "and there you have it :)" +print("and there you have it :)") diff --git a/examples/helpers/__init__.py b/examples/helpers/__init__.py index 3018e1b..81e763c 100644 --- a/examples/helpers/__init__.py +++ b/examples/helpers/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import simplejson as json import requests diff --git a/examples/orders.py b/examples/orders.py index 89bc27c..9ec0a8d 100644 --- a/examples/orders.py +++ b/examples/orders.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import balanced @@ -56,6 +56,6 @@ try: another_credit = another_bank_account.credit(amount=50, order=order) except balanced.exc.BalancedError as ex: - print ex + print(ex) assert ex is not None diff --git a/render_scenarios.py b/render_scenarios.py index 261c406..3608ea9 100644 --- a/render_scenarios.py +++ b/render_scenarios.py @@ -22,13 +22,13 @@ def construct_response(scenario_name): text = template.render(response=response).strip() response = json.loads(text) del response["links"] - for key, value in response.items(): + for key, value in list(response.items()): response = value[0] type = key resource = balanced.Resource() object_type = resource.registry[type] object_instance = object_type() - for key, value in response.items(): + for key, value in list(response.items()): setattr(object_instance, key, value) text = template.render(response=object_instance) except KeyError: @@ -50,8 +50,8 @@ def render_executables(): request=request, payload=payload).strip() except KeyError: text = '' - print "WARN: Skipped {} since {} not in scenario.cache".format( - path, event_name) + print("WARN: Skipped {} since {} not in scenario.cache".format( + path, event_name)) with open(os.path.join(os.path.dirname(path), 'executable.py'), 'w+') as write_to: write_to.write(text) @@ -76,15 +76,15 @@ def issue_no_mako_warnings(): set_has_mako.add(os.path.dirname(path)) for path in glob2.glob('./scenarios/**/python.mako'): set_no_python_mako.add(os.path.dirname(path)) - print 'The following dont have a python.mako file. Look into it!' - print set_has_mako.difference(set_no_python_mako) + print('The following dont have a python.mako file. Look into it!') + print(set_has_mako.difference(set_no_python_mako)) if __name__ == "__main__": - print "Making Executables" + print("Making Executables") render_executables() - print "Rendering new mako files" + print("Rendering new mako files") render_mako() issue_no_mako_warnings() diff --git a/requirements.txt b/requirements.txt index 556ea63..2269c00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -wac==0.22 +-e git+https://github.com/zmike808/wac.git#egg=wac iso8601==0.1.4 uritemplate==0.6 diff --git a/test-requirements.txt b/test-requirements.txt index e824df9..d7d79b4 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,4 @@ nose nose-setenv mock -unittest2 +unittest2py3k diff --git a/tests/acceptance_suite.py b/tests/acceptance_suite.py index f57327b..3d39e17 100644 --- a/tests/acceptance_suite.py +++ b/tests/acceptance_suite.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + from datetime import date import inspect import time @@ -9,7 +9,7 @@ import requests import unittest2 as unittest -from fixtures import cards, merchants, bank_accounts +from .fixtures import cards, merchants, bank_accounts class TestCases(unittest.TestCase): @@ -430,7 +430,7 @@ def _test_transaction_successes(self, cases): resource = callable(**kwargs) if validate_cls is not None: self.assertTrue(isinstance(resource, validate_cls)) - for k, v in validate_kwargs.iteritems(): + for k, v in validate_kwargs.items(): resource_v = getattr(resource, k) if isinstance(v, balanced.Resource): self.assertEqual(v.id, resource_v.id) @@ -851,7 +851,7 @@ def test_verify_test_bank_account_failure(self): **bank_accounts.BANK_ACCOUNT).save() verification = bank_account.verify() ex = balanced.exc.FundingInstrumentVerificationFailure - for _ in xrange(verification.remaining_attempts): + for _ in range(verification.remaining_attempts): with self.assertRaises(ex): verification.confirm(1, 2) with self.assertRaises(ex): diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index b20e256..bdd283b 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import os import simplejson as json @@ -13,5 +13,5 @@ def __getattr__(cls, item): )) -class Resources(object): - __metaclass__ = ResourceMeta +class Resources(object, metaclass=ResourceMeta): + pass diff --git a/tests/test_balanced.py b/tests/test_balanced.py index 3c7e978..5dac2a7 100644 --- a/tests/test_balanced.py +++ b/tests/test_balanced.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from tests.utils import TestCase @@ -12,6 +12,6 @@ def test_import_star(self): # the __import__ doesn't do what I want here. # and doing a "from balanced import *" generates an # unsupressable SyntaxWarning. - exec "from balanced import *" # pylint: disable-msg=W0122 + exec("from balanced import *") # pylint: disable-msg=W0122 except Exception as exc: raise ImportError("%s" % exc) diff --git a/tests/test_client.py b/tests/test_client.py index d9242da..cbef69b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import balanced @@ -14,7 +14,7 @@ def test_configure(self): expected_headers = { 'content-type': 'application/json;revision=1.1', 'accept': 'application/vnd.api+json;revision=1.1', - 'User-Agent': u'balanced-python/' + balanced.__version__, + 'User-Agent': 'balanced-python/' + balanced.__version__, } self.assertDictContainsSubset( expected_headers, balanced.config.client.config.headers diff --git a/tests/test_resource.py b/tests/test_resource.py index acb97ab..9b65c43 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import balanced diff --git a/tests/test_suite.py b/tests/test_suite.py index aacddf5..3ae1fc1 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + import sys import time from datetime import date @@ -183,7 +183,7 @@ def test_create_hold_and_capture_it(self): def test_create_a_person_customer(self): customer = balanced.Customer(**PERSON).save() - for key, value in PERSON.iteritems(): + for key, value in PERSON.items(): if key == 'dob': continue if isinstance(value, dict): @@ -193,7 +193,7 @@ def test_create_a_person_customer(self): def test_create_a_business_customer(self): customer = balanced.Customer(**BUSINESS).save() - for key, value in BUSINESS.iteritems(): + for key, value in BUSINESS.items(): if key == 'dob': continue if isinstance(value, dict): @@ -384,11 +384,11 @@ def test_dispute(self): # TODO: this is ugly, I think we should provide a more # reliable way to generate dispute, at least it should not # take this long - print >> sys.stderr, ( + print(( 'It takes a while before the dispute record created, ' 'take and nap and wake up, then it should be done :/ ' '(last time I tried it took 10 minutes...)' - ) + ), file=sys.stderr) timeout = 12 * 60 interval = 10 begin = time.time() @@ -397,7 +397,7 @@ def test_dispute(self): break time.sleep(interval) elapsed = time.time() - begin - print >> sys.stderr, 'Polling disputes..., elapsed', elapsed + print('Polling disputes..., elapsed', elapsed, file=sys.stderr) self.assertLess(elapsed, timeout, 'Ouch, timeout') dispute = balanced.Dispute.query.one() diff --git a/tests/utils.py b/tests/utils.py index 1948ebc..8fd4926 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import contextlib import multiprocessing