Skip to content

Commit d493b20

Browse files
committed
Update project configuration and dependencies
- Changed version string format in payjp/version.py from single quotes to double quotes. - Added pyproject.toml for build system and linting configuration with Ruff. - Created requirements-dev.txt for development dependencies including pytest, mock, ruff, and tox. - Updated setup.py to use double quotes and removed deprecated Python version check. - Modified tox.ini to include a new environment for Ruff linting and formatting checks.
1 parent 435f679 commit d493b20

18 files changed

+1298
-1257
lines changed

.github/workflows/ruff.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Ruff
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
ruff:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v3
10+
- name: Set up Python
11+
uses: actions/setup-python@v4
12+
with:
13+
python-version: '3.8'
14+
cache: 'pip'
15+
- name: Install dependencies
16+
run: |
17+
python -m pip install --upgrade pip
18+
pip install ruff
19+
- name: Lint with ruff
20+
run: |
21+
ruff check .
22+
- name: Format with ruff
23+
run: |
24+
ruff format --check .

payjp/__init__.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Configuration variables
44

55
api_key = None
6-
api_base = 'https://api.pay.jp'
6+
api_base = "https://api.pay.jp"
77
api_version = None
88

99
max_retry = 0
@@ -12,22 +12,33 @@
1212

1313
# TODO include Card?
1414
__all__ = [
15-
'Account',
16-
'Card',
17-
'Charge',
18-
'Customer',
19-
'Event',
20-
'Plan',
21-
'Subscription',
22-
'Token',
23-
'Transfer',
24-
'Statement',
25-
'Term',
26-
'Balance',
27-
'ThreeDSecureRequest'
15+
"Account",
16+
"Card",
17+
"Charge",
18+
"Customer",
19+
"Event",
20+
"Plan",
21+
"Subscription",
22+
"Token",
23+
"Transfer",
24+
"Statement",
25+
"Term",
26+
"Balance",
27+
"ThreeDSecureRequest",
2828
]
2929

3030
# Resource
3131
from payjp.resource import ( # noqa
32-
Account, Charge, Customer, Event, Plan, Subscription, Token, Transfer, Statement, Term, Balance, ThreeDSecureRequest)
33-
32+
Account,
33+
Charge,
34+
Customer,
35+
Event,
36+
Plan,
37+
Subscription,
38+
Token,
39+
Transfer,
40+
Statement,
41+
Term,
42+
Balance,
43+
ThreeDSecureRequest,
44+
)

payjp/api_requestor.py

Lines changed: 79 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
1-
# coding: utf-8
2-
31
import base64
42
import calendar
53
import datetime
64
import json
75
import logging
86
import platform
9-
import time
107
import random
8+
import time
119
from urllib.parse import urlencode, urlsplit, urlunsplit
1210

1311
import payjp
14-
from . import (
15-
error,
16-
http_client,
17-
version,
18-
)
1912

20-
logger = logging.getLogger('payjp')
13+
from . import error, http_client, version
2114

15+
logger = logging.getLogger("payjp")
2216

23-
class APIRequestor(object):
2417

18+
class APIRequestor:
2519
def __init__(self, key=None, client=None, api_base=None, account=None):
2620
if api_base:
2721
self.api_base = api_base
@@ -38,142 +32,161 @@ def _get_retry_delay(self, retry_count):
3832
Based on "Exponential backoff with equal jitter" algorithm.
3933
https://aws.amazon.com/jp/blogs/architecture/exponential-backoff-and-jitter/
4034
"""
41-
wait = min(payjp.retry_max_delay, payjp.retry_initial_delay * 2 ** retry_count)
42-
return (wait / 2 + random.uniform(0, wait / 2))
35+
wait = min(
36+
payjp.retry_max_delay,
37+
payjp.retry_initial_delay * 2**retry_count,
38+
)
39+
return wait / 2 + random.uniform(0, wait / 2)
4340

4441
def request(self, method, url, params=None, headers=None):
4542
max_retry = payjp.max_retry or 0
4643
for i in range(max_retry + 1):
4744
body, code, my_api_key = self.request_raw(
48-
method.lower(), url, params, headers)
45+
method.lower(), url, params, headers
46+
)
4947
if code != 429:
5048
break
5149
elif i != max_retry:
5250
wait = self._get_retry_delay(i)
53-
logger.debug('Retry after %s seconds.' % wait)
51+
logger.debug("Retry after %s seconds." % wait)
5452
time.sleep(wait)
5553

5654
response = self.interpret_response(body, code)
5755
return response, my_api_key
5856

5957
def handle_api_error(self, body, code, response):
6058
try:
61-
err = response['error']
59+
err = response["error"]
6260
except (KeyError, TypeError):
6361
raise error.APIError(
6462
"Invalid response object from API: %r (HTTP response code "
6563
"was %d)" % (body, code),
66-
body, code, response)
64+
body,
65+
code,
66+
response,
67+
)
6768

6869
if code in [400, 404]:
6970
raise error.InvalidRequestError(
70-
err.get('message'), err.get('param'), body, code, response)
71+
err.get("message"), err.get("param"), body, code, response
72+
)
7173
elif code == 401:
72-
raise error.AuthenticationError(
73-
err.get('message'), body, code, response)
74+
raise error.AuthenticationError(err.get("message"), body, code, response)
7475
elif code == 402:
75-
raise error.CardError(err.get('message'), err.get('param'),
76-
err.get('code'), body, code, response)
76+
raise error.CardError(
77+
err.get("message"),
78+
err.get("param"),
79+
err.get("code"),
80+
body,
81+
code,
82+
response,
83+
)
7784
else:
78-
raise error.APIError(err.get('message'), body, code, response)
85+
raise error.APIError(err.get("message"), body, code, response)
7986

8087
def request_raw(self, method, url, params=None, supplied_headers=None):
81-
8288
from payjp import api_version
8389

8490
if self.api_key:
8591
my_api_key = self.api_key
8692
else:
8793
from payjp import api_key
94+
8895
my_api_key = api_key
8996

9097
if my_api_key is None:
9198
raise error.AuthenticationError(
92-
'No API key provided. (HINT: set your API key using '
99+
"No API key provided. (HINT: set your API key using "
93100
'"payjp.api_key = <API-KEY>"). You can generate API keys '
94-
'from the Payjp web interface. See https://docs.pay.jp'
95-
'for details, or email support@pay.jp if you have any '
96-
'questions.')
101+
"from the Payjp web interface. See https://docs.pay.jp"
102+
"for details, or email support@pay.jp if you have any "
103+
"questions."
104+
)
97105

98-
abs_url = '%s%s' % (self.api_base, url)
106+
abs_url = f"{self.api_base}{url}"
99107

100108
encoded_params = urlencode(list(_api_encode(params or {})))
101109

102-
if method in ('get', 'delete'):
110+
if method in ("get", "delete"):
103111
if params:
104112
abs_url = _build_api_url(abs_url, encoded_params)
105113
post_data = None
106-
elif method == 'post':
114+
elif method == "post":
107115
post_data = encoded_params
108116
else:
109-
raise error.APIConnectionError(
110-
'Unrecognized HTTP method %r.' % (method,))
117+
raise error.APIConnectionError(f"Unrecognized HTTP method {method!r}.")
111118

112119
ua = {
113-
'bindings_version': version.VERSION,
114-
'lang': 'python',
115-
'publisher': 'payjp',
116-
'httplib': self._client.name,
120+
"bindings_version": version.VERSION,
121+
"lang": "python",
122+
"publisher": "payjp",
123+
"httplib": self._client.name,
117124
}
118125

119-
for attr, func in [['lang_version', platform.python_version],
120-
['platform', platform.platform],
121-
['uname', lambda: ' '.join(platform.uname())]]:
126+
for attr, func in [
127+
["lang_version", platform.python_version],
128+
["platform", platform.platform],
129+
["uname", lambda: " ".join(platform.uname())],
130+
]:
122131
try:
123132
val = func()
124133
except Exception as e:
125-
val = '!! %s' % (e,)
134+
val = f"!! {e}"
126135
ua[attr] = val
127136

128137
encoded_api_key = str(
129-
base64.b64encode(
130-
bytes(''.join([my_api_key, ':']), 'utf-8')), 'utf-8')
138+
base64.b64encode(bytes("".join([my_api_key, ":"]), "utf-8")), "utf-8"
139+
)
131140

132141
headers = {
133-
'X-Payjp-Client-User-Agent': json.dumps(ua),
134-
'User-Agent': 'Payjp/v1 PythonBindings/%s' % (version.VERSION,),
135-
'Authorization': 'Basic %s' % encoded_api_key
142+
"X-Payjp-Client-User-Agent": json.dumps(ua),
143+
"User-Agent": f"Payjp/v1 PythonBindings/{version.VERSION}",
144+
"Authorization": "Basic %s" % encoded_api_key,
136145
}
137146

138147
if self.payjp_account:
139-
headers['Payjp-Account'] = self.payjp_account
148+
headers["Payjp-Account"] = self.payjp_account
140149

141-
if method == 'post':
142-
headers['Content-Type'] = 'application/x-www-form-urlencoded'
150+
if method == "post":
151+
headers["Content-Type"] = "application/x-www-form-urlencoded"
143152

144153
if api_version is not None:
145-
headers['Payjp-Version'] = api_version
154+
headers["Payjp-Version"] = api_version
146155

147156
if supplied_headers is not None:
148157
for key, value in supplied_headers.items():
149158
headers[key] = value
150159

151-
body, code = self._client.request(
152-
method, abs_url, headers, post_data)
160+
body, code = self._client.request(method, abs_url, headers, post_data)
153161

154-
logger.info('%s %s %d', method.upper(), abs_url, code)
162+
logger.info("%s %s %d", method.upper(), abs_url, code)
155163
logger.debug(
156-
'API request to %s returned (response code, response body) of '
157-
'(%d, %r)',
158-
abs_url, code, body)
164+
"API request to %s returned (response code, response body) of (%d, %r)",
165+
abs_url,
166+
code,
167+
body,
168+
)
159169

160170
return body, code, my_api_key
161171

162172
def interpret_response(self, body, code):
163173
try:
164-
if hasattr(body, 'decode'):
165-
body = body.decode('utf-8')
174+
if hasattr(body, "decode"):
175+
body = body.decode("utf-8")
166176
response = json.loads(body)
167177
except Exception:
168178
raise error.APIError(
169179
"Invalid response body from API: %s "
170180
"(HTTP response code was %d)" % (body, code),
171-
body, code)
181+
body,
182+
code,
183+
)
172184
if not (200 <= code < 300):
173185
self.handle_api_error(body, code, response)
174186

175187
return response
176188

189+
177190
def _encode_datetime(dttime):
178191
if dttime.tzinfo and dttime.tzinfo.utcoffset(dttime) is not None:
179192
utc_timestamp = calendar.timegm(dttime.utctimetuple())
@@ -182,29 +195,32 @@ def _encode_datetime(dttime):
182195

183196
return int(utc_timestamp)
184197

198+
185199
def _api_encode(data):
186200
for key, value in data.items():
187201
if value is None:
188202
continue
189-
elif hasattr(value, 'payjp_id'):
203+
elif hasattr(value, "payjp_id"):
190204
yield (key, value.payjp_id)
191205
elif isinstance(value, list) or isinstance(value, tuple):
192206
for subvalue in value:
193-
yield ("%s[]" % (key,), subvalue)
207+
yield (f"{key}[]", subvalue)
194208
elif isinstance(value, dict):
195-
subdict = dict(('%s[%s]' % (key, subkey), subvalue) for
196-
subkey, subvalue in value.items())
209+
subdict = dict(
210+
(f"{key}[{subkey}]", subvalue) for subkey, subvalue in value.items()
211+
)
197212
for subkey, subvalue in _api_encode(subdict):
198213
yield (subkey, subvalue)
199214
elif isinstance(value, datetime.datetime):
200215
yield (key, _encode_datetime(value))
201216
else:
202217
yield (key, value)
203218

219+
204220
def _build_api_url(url, query):
205221
scheme, netloc, path, base_query, fragment = urlsplit(url)
206222

207223
if base_query:
208-
query = '%s&%s' % (base_query, query)
224+
query = f"{base_query}&{query}"
209225

210226
return urlunsplit((scheme, netloc, path, query, fragment))

0 commit comments

Comments
 (0)