Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stripe-python v6 release #1001

Merged
merged 11 commits into from
Aug 16, 2023
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ jobs:
"3.8",
"3.9",
"3.10",
"pypy-2.7",
"pypy-3.7",
"pypy-3.8",
]
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ python setup.py install

### Requirements

- Python 2.7+ or Python 3.6+ (PyPy supported)
- Python 3.6+ (PyPy supported)

#### Python 2.7 deprecation

[The Python Software Foundation (PSF)](https://www.python.org/psf-landing/) community [announced the end of support of Python 2](https://www.python.org/doc/sunset-python-2/) on 01 January 2020.
Starting with version 6.0.0 Stripe SDK Python packages will no longer support Python 2.7. To continue to get new features and security updates, please make sure to update your Python runtime to Python 3.6+.

The last version of the Stripe SDK that supports Python 2.7 is 5.5.0.

## Usage

Expand Down Expand Up @@ -162,7 +169,7 @@ There are a few options for enabling it:

### Accessing response code and headers

You can access the HTTP response code and headers using the `last_response` property of the returned resource.
You can access the HTTP response code and headers using the `last_response` property of the returned resource.

```python
customer = stripe.Customer.retrieve(
Expand Down
5 changes: 1 addition & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
zip_safe=False,
install_requires=[
'requests >= 2.20; python_version >= "3.0"',
'requests[security] >= 2.20; python_version < "3.0"',
],
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
python_requires=">=3.6",
project_urls={
"Bug Tracker": "https://github.com/stripe/stripe-python/issues",
"Changes": "https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md",
Expand All @@ -47,8 +46,6 @@
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
Expand Down
3 changes: 2 additions & 1 deletion stripe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
# Andrew Metcalf <andrew@stripe.com>

# Configuration variables
from stripe.api_version import _ApiVersion

api_key = None
client_id = None
api_base = "https://api.stripe.com"
connect_api_base = "https://connect.stripe.com"
upload_api_base = "https://files.stripe.com"
api_version = None
api_version = _ApiVersion.CURRENT
verify_ssl_certs = True
proxy = None
default_http_client = None
Expand Down
17 changes: 8 additions & 9 deletions stripe/api_requestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from collections import OrderedDict

import stripe
from stripe import error, oauth_error, http_client, version, util, six
from stripe import error, oauth_error, http_client, version, util
from stripe.multipart_data_generator import MultipartDataGenerator
from stripe.six.moves.urllib.parse import urlencode, urlsplit, urlunsplit
from urllib.parse import urlencode, urlsplit, urlunsplit
from stripe.stripe_response import StripeResponse, StripeStreamResponse


Expand All @@ -27,14 +27,13 @@ def _encode_datetime(dttime):

def _encode_nested_dict(key, data, fmt="%s[%s]"):
d = OrderedDict()
for subkey, subvalue in six.iteritems(data):
for subkey, subvalue in data.items():
d[fmt % (key, subkey)] = subvalue
return d


def _api_encode(data):
for key, value in six.iteritems(data):
key = util.utf8(key)
for key, value in data.items():
if value is None:
continue
elif hasattr(value, "stripe_id"):
Expand All @@ -46,15 +45,15 @@ def _api_encode(data):
for k, v in _api_encode(subdict):
yield (k, v)
else:
yield ("%s[%d]" % (key, i), util.utf8(sv))
yield ("%s[%d]" % (key, i), sv)
elif isinstance(value, dict):
subdict = _encode_nested_dict(key, value)
for subkey, subvalue in _api_encode(subdict):
yield (subkey, subvalue)
elif isinstance(value, datetime.datetime):
yield (key, _encode_datetime(value))
else:
yield (key, util.utf8(value))
yield (key, value)


def _build_api_url(url, query):
Expand Down Expand Up @@ -146,7 +145,7 @@ def handle_error_response(self, rbody, rcode, resp, rheaders):
# OAuth errors are a JSON object where `error` is a string. In
# contrast, in API errors, `error` is a hash with sub-keys. We use
# this property to distinguish between OAuth and API errors.
if isinstance(error_data, six.string_types):
if isinstance(error_data, str):
err = self.specific_oauth_error(
rbody, rcode, resp, rheaders, error_data
)
Expand Down Expand Up @@ -344,7 +343,7 @@ def request_raw(

headers = self.request_headers(my_api_key, method)
if supplied_headers is not None:
for key, value in six.iteritems(supplied_headers):
for key, value in supplied_headers.items():
headers[key] = value

util.log_info("Request to Stripe api", method=method, path=abs_url)
Expand Down
1 change: 0 additions & 1 deletion stripe/api_resources/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from stripe.api_resources.abstract.custom_method import custom_method

from stripe.api_resources.abstract.test_helpers import (
test_helpers,
APIResourceTestHelpers,
)

Expand Down
7 changes: 3 additions & 4 deletions stripe/api_resources/abstract/api_resource.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import absolute_import, division, print_function

from stripe import api_requestor, error, util, six
from stripe import api_requestor, error, util
from stripe.stripe_object import StripeObject
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


class APIResource(StripeObject):
Expand Down Expand Up @@ -30,15 +30,14 @@ def class_url(cls):
def instance_url(self):
id = self.get("id")

if not isinstance(id, six.string_types):
if not isinstance(id, str):
raise error.InvalidRequestError(
"Could not determine which URL to request: %s instance "
"has invalid ID: %r, %s. ID should be of type `str` (or"
" `unicode`)" % (type(self).__name__, id, type(id)),
"id",
)

id = util.utf8(id)
base = self.class_url()
extn = quote_plus(id)
return "%s/%s" % (base, extn)
Expand Down
6 changes: 3 additions & 3 deletions stripe/api_resources/abstract/custom_method.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import absolute_import, division, print_function

from stripe import util
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


def custom_method(name, http_verb, http_path=None, is_streaming=False):
Expand All @@ -17,7 +17,7 @@ def wrapper(cls):
def custom_method_request(cls, sid, **params):
url = "%s/%s/%s" % (
cls.class_url(),
quote_plus(util.utf8(sid)),
quote_plus(sid),
http_path,
)
obj = cls._static_request(http_verb, url, params=params)
Expand All @@ -32,7 +32,7 @@ def custom_method_request(cls, sid, **params):
def custom_method_request_stream(cls, sid, **params):
url = "%s/%s/%s" % (
cls.class_url(),
quote_plus(util.utf8(sid)),
quote_plus(sid),
http_path,
)
return cls._static_request_stream(http_verb, url, params=params)
Expand Down
4 changes: 2 additions & 2 deletions stripe/api_resources/abstract/deletable_api_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from stripe import util
from stripe.api_resources.abstract.api_resource import APIResource
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


class DeletableAPIResource(APIResource):
@classmethod
def _cls_delete(cls, sid, **params):
url = "%s/%s" % (cls.class_url(), quote_plus(util.utf8(sid)))
url = "%s/%s" % (cls.class_url(), quote_plus(sid))
return cls._static_request("delete", url, params=params)

@util.class_method_variant("_cls_delete")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import absolute_import, division, print_function

from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus

from stripe.api_resources.abstract import APIResource

Expand Down
32 changes: 3 additions & 29 deletions stripe/api_resources/abstract/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from __future__ import absolute_import, division, print_function

from stripe import error, util, six
from stripe.six.moves.urllib.parse import quote_plus
from stripe.api_resources.abstract import APIResource
from stripe import error
from urllib.parse import quote_plus


class APIResourceTestHelpers:
Expand Down Expand Up @@ -42,39 +41,14 @@ def class_url(cls):
def instance_url(self):
id = self.resource.get("id")

if not isinstance(id, six.string_types):
if not isinstance(id, str):
raise error.InvalidRequestError(
"Could not determine which URL to request: %s instance "
"has invalid ID: %r, %s. ID should be of type `str` (or"
" `unicode`)" % (type(self).__name__, id, type(id)),
"id",
)

id = util.utf8(id)
base = self.class_url()
extn = quote_plus(id)
return "%s/%s" % (base, extn)


# TODO (next major)
def test_helpers(cls):
"""
The test_helpers decorator is deprecated and will be removed in a future version
of the library.
"""

def test_helpers_getter(self):
return self.TestHelpers(self)

if not issubclass(cls, APIResource):
raise ValueError(
"Could not apply @test_helpers decorator to %r."
" The class should a subclass of APIResource." % cls
)

cls.TestHelpers._resource_cls = cls
cls.TestHelpers._static_request = cls._static_request
cls.TestHelpers._static_request_stream = cls._static_request_stream

cls.test_helpers = property(test_helpers_getter)
return cls
4 changes: 2 additions & 2 deletions stripe/api_resources/abstract/updateable_api_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from stripe import util
from stripe.api_resources.abstract.api_resource import APIResource
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


class UpdateableAPIResource(APIResource):
@classmethod
def modify(cls, sid, **params):
url = "%s/%s" % (cls.class_url(), quote_plus(util.utf8(sid)))
url = "%s/%s" % (cls.class_url(), quote_plus(sid))
return cls._static_request("post", url, params=params)

def save(self, idempotency_key=None):
Expand Down
11 changes: 5 additions & 6 deletions stripe/api_resources/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# File generated from our OpenAPI spec
from __future__ import absolute_import, division, print_function

import stripe
from stripe import oauth, six
from stripe import api_resources
from stripe import oauth
from stripe import util
from stripe.api_resources.abstract import CreateableAPIResource
from stripe.api_resources.abstract import DeletableAPIResource
from stripe.api_resources.abstract import ListableAPIResource
from stripe.api_resources.abstract import UpdateableAPIResource
from stripe.api_resources.abstract import nested_resource_class_methods
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


@nested_resource_class_methods(
Expand Down Expand Up @@ -129,7 +129,6 @@ def modify(cls, id=None, **params):
def _build_instance_url(cls, sid):
if not sid:
return "/v1/account"
sid = util.utf8(sid)
base = cls.class_url()
extn = quote_plus(sid)
return "%s/%s" % (base, extn)
Expand All @@ -145,10 +144,10 @@ def serialize(self, previous):
params = super(Account, self).serialize(previous)
previous = previous or self._previous or {}

for k, v in six.iteritems(self):
for k, v in iter(self.items()):
if (
k == "individual"
and isinstance(v, stripe.api_resources.Person)
and isinstance(v, api_resources.Person)
and k not in params
):
params[k] = v.serialize(previous.get(k, None))
Expand Down
5 changes: 1 addition & 4 deletions stripe/api_resources/application_fee_refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
# File generated from our OpenAPI spec
from __future__ import absolute_import, division, print_function

from stripe import util
from stripe.api_resources import ApplicationFee
from stripe.api_resources.abstract import UpdateableAPIResource
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


class ApplicationFeeRefund(UpdateableAPIResource):
Expand All @@ -21,8 +20,6 @@ class ApplicationFeeRefund(UpdateableAPIResource):

@classmethod
def _build_instance_url(cls, fee, sid):
fee = util.utf8(fee)
sid = util.utf8(sid)
base = ApplicationFee.class_url()
cust_extn = quote_plus(fee)
extn = quote_plus(sid)
Expand Down
9 changes: 4 additions & 5 deletions stripe/api_resources/bank_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
from __future__ import absolute_import, division, print_function

from stripe import error
from stripe import util
from stripe.api_resources.abstract import DeletableAPIResource
from stripe.api_resources.abstract import UpdateableAPIResource
from stripe.api_resources.abstract import VerifyMixin
from stripe.api_resources.account import Account
from stripe.api_resources.customer import Customer
from stripe.six.moves.urllib.parse import quote_plus
from urllib.parse import quote_plus


class BankAccount(DeletableAPIResource, UpdateableAPIResource, VerifyMixin):
Expand All @@ -26,17 +25,17 @@ class BankAccount(DeletableAPIResource, UpdateableAPIResource, VerifyMixin):
OBJECT_NAME = "bank_account"

def instance_url(self):
token = util.utf8(self.id)
token = self.id
extn = quote_plus(token)
if hasattr(self, "customer"):
customer = util.utf8(self.customer)
customer = self.customer

base = Customer.class_url()
owner_extn = quote_plus(customer)
class_base = "sources"

elif hasattr(self, "account"):
account = util.utf8(self.account)
account = self.account

base = Account.class_url()
owner_extn = quote_plus(account)
Expand Down
Loading