Skip to content

Commit

Permalink
Swap to Ruff
Browse files Browse the repository at this point in the history
  • Loading branch information
jarekwg committed Nov 24, 2024
1 parent 75757d9 commit 74107a4
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 112 deletions.
38 changes: 3 additions & 35 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,8 @@ on:
pull_request: ~

jobs:
flake8:
ruff:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install deps
run: pip install flake8
- name: Flake8
run: flake8

isort:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install deps
run: pip install isort
- name: Isort
run: isort --diff --check-only .

# superlinter:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v2
# - name: Run Superlinter
# uses: docker://github/super-linter:latest
# env:
# VALIDATE_PYTHON: true
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v1
2 changes: 0 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ jobs:
strategy:
matrix:
python:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
Expand Down
31 changes: 29 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies = [
"requests>=2.32.0",
"requests-oauthlib>=2.0.0",
]
requires-python = ">= 3.8"
requires-python = ">= 3.10"
authors = [
{name = "Jarek Głowacki", email = "[email protected]"}
]
Expand All @@ -35,4 +35,31 @@ source = "https://github.com/uptick/pymyob"
releasenotes = "https://github.com/uptick/pymyob/releases"

[tool.hatch.build.targets.wheel]
packages = ["src/myob"]
packages = ["src/myob"]

[tool.ruff]
target-version = "py312"
line-length = 100

[tool.ruff.lint]
select = [
"F", # Pyflakes
"E", # pycodestyle
"W", # pycodestyle
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
# "ANN", # flake8-annotations
"B", # flake8-bugbear
"S", # flake8-bandit
"T10", # debugger
"TID", # flake8-tidy-imports
]
ignore = [
"E501"
]

[tool.ruff.lint.isort]
extra-standard-library = [
"requests",
]
9 changes: 0 additions & 9 deletions setup.cfg

This file was deleted.

11 changes: 3 additions & 8 deletions src/myob/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ class Myob:

def __init__(self, credentials):
if not isinstance(credentials, PartnerCredentials):
raise TypeError(
f"Expected a Credentials instance, got {type(credentials).__name__}."
)
raise TypeError(f"Expected a Credentials instance, got {type(credentials).__name__}.")
self.credentials = credentials
self.companyfiles = CompanyFiles(credentials)
self._manager = Manager(
Expand Down Expand Up @@ -49,8 +47,7 @@ def __init__(self, credentials):
def all(self):
raw_companyfiles = self._manager.all()
return [
CompanyFile(raw_companyfile, self.credentials)
for raw_companyfile in raw_companyfiles
CompanyFile(raw_companyfile, self.credentials) for raw_companyfile in raw_companyfiles
]

def get(self, id, call=True):
Expand All @@ -60,9 +57,7 @@ def get(self, id, call=True):
# on the GET endpoint. The only way we currently allow passing company_id is by setting it on the manager,
# and we can't do that on init, as this is a manager for company files plural..
# Reluctant to change manager code, as it would add confusion if the inner method let you override the company_id.
manager = Manager(
"", self.credentials, raw_endpoints=[(GET, "", "")], company_id=id
)
manager = Manager("", self.credentials, raw_endpoints=[(GET, "", "")], company_id=id)
raw_companyfile = manager.get()["CompanyFile"]
else:
raw_companyfile = {"Id": id}
Expand Down
2 changes: 1 addition & 1 deletion src/myob/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
MYOB_PARTNER_BASE_URL = "https://secure.myob.com/oauth2/"

AUTHORIZE_URL = "account/authorize/"
ACCESS_TOKEN_URL = "v1/authorize/"
ACCESS_TOKEN_URL = "v1/authorize/" # noqa: S105

DEFAULT_PAGE_SIZE = 400

Expand Down
21 changes: 7 additions & 14 deletions src/myob/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(
consumer_secret,
callback_uri,
verified=False,
companyfile_credentials={},
companyfile_credentials={}, # noqa: B006
oauth_token=None,
refresh_token=None,
oauth_expires_at=None,
Expand All @@ -32,24 +32,19 @@ def __init__(
self.refresh_token = refresh_token

if oauth_expires_at is not None:
assert isinstance(
oauth_expires_at, datetime.datetime
), "'oauth_expires_at' must be a datetime instance."
if not isinstance(oauth_expires_at, datetime.datetime):
raise ValueError("'oauth_expires_at' must be a datetime instance.")
self.oauth_expires_at = oauth_expires_at

self._oauth = OAuth2Session(consumer_key, redirect_uri=callback_uri)
url, _ = self._oauth.authorization_url(
MYOB_PARTNER_BASE_URL + AUTHORIZE_URL, state=state
)
url, _ = self._oauth.authorization_url(MYOB_PARTNER_BASE_URL + AUTHORIZE_URL, state=state)
self.url = url + "&scope=CompanyFile"

# TODO: Add `verify` kwarg here, which will quickly throw the provided credentials at a
# protected endpoint to ensure they are valid. If not, raise appropriate error.
def authenticate_companyfile(self, company_id, username, password):
"""Store hashed username-password for logging into company file."""
userpass = base64.b64encode(bytes(f"{username}:{password}", "utf-8")).decode(
"utf-8"
)
userpass = base64.b64encode(bytes(f"{username}:{password}", "utf-8")).decode("utf-8")
self.companyfile_credentials[company_id] = userpass

@property
Expand Down Expand Up @@ -79,12 +74,10 @@ def expired(self, now=None):
# Allow a bit of time for clock differences and round trip times
# to prevent false negatives. If users want the precise expiry,
# they can use self.oauth_expires_at
CONSERVATIVE_SECONDS = 30
CONSERVATIVE_SECONDS = 30 # noqa: N806

now = now or datetime.datetime.now()
return self.oauth_expires_at <= (
now + datetime.timedelta(seconds=CONSERVATIVE_SECONDS)
)
return self.oauth_expires_at <= (now + datetime.timedelta(seconds=CONSERVATIVE_SECONDS))

def verify(self, code):
"""Verify an OAuth session, retrieving an access token."""
Expand Down
4 changes: 1 addition & 3 deletions src/myob/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
CRUD = (
"CRUD" # shorthand for creating the ALL|GET|POST|PUT|DELETE endpoints in one swoop
)
CRUD = "CRUD" # shorthand for creating the ALL|GET|POST|PUT|DELETE endpoints in one swoop

METHOD_ORDER = [ALL, GET, POST, PUT, DELETE]

Expand Down
2 changes: 1 addition & 1 deletion src/myob/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class MyobException(Exception):
class MyobException(Exception): # noqa: N818
def __init__(self, response, msg=None):
self.response = response
try:
Expand Down
4 changes: 2 additions & 2 deletions src/myob/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


class Manager:
def __init__(self, name, credentials, company_id=None, endpoints=[], raw_endpoints=[]):
def __init__(self, name, credentials, company_id=None, endpoints=[], raw_endpoints=[]): # noqa: B006
self.credentials = credentials
self.name = "_".join(p for p in name.rstrip("/").split("/") if "[" not in p)
self.base_url = MYOB_BASE_URL
Expand Down Expand Up @@ -192,7 +192,7 @@ def build_value(value):
if k.endswith(f"__{op}"):
k = k[:-4]
operator = op
if not isinstance(v, (list, tuple)):
if not isinstance(v, list | tuple):
v = [v]
filters.append(" or ".join(f"{k} {operator} {build_value(v_)}" for v_ in v))

Expand Down
30 changes: 8 additions & 22 deletions tests/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class EndpointTests(TestCase):
def setUp(self):
cred = PartnerCredentials(
consumer_key="KeyToTheKingdom",
consumer_secret="TellNoOne",
consumer_secret="TellNoOne", # noqa: S106
callback_uri="CallOnlyWhenCalledTo",
companyfile_credentials={CID: "!encoded-userpass="},
)
Expand All @@ -40,9 +40,7 @@ def setUp(self):
}

@patch("myob.managers.requests.request")
def assertEndpointReached(
self, func, params, method, endpoint, mock_request, timeout=None
):
def assertEndpointReached(self, func, params, method, endpoint, mock_request, timeout=None): # noqa: N802
mock_request.return_value.status_code = 200
if endpoint == f"/{CID}/":
mock_request.return_value.json.return_value = {"CompanyFile": {"Id": CID}}
Expand All @@ -58,9 +56,7 @@ def assertEndpointReached(
)

@patch("myob.managers.requests.request")
def assertExceptionHandled(
self, status_code, response_json, exception, mock_request
):
def assertExceptionHandled(self, status_code, response_json, exception, mock_request): # noqa: N802
mock_request.return_value.status_code = status_code
mock_request.return_value.json.return_value = response_json
with self.assertRaises(exception):
Expand Down Expand Up @@ -121,9 +117,7 @@ def test_companyfiles(self):
" get(id) - List endpoints available for a company file."
),
)
self.assertEndpointReached(
self.myob.companyfiles.get, {"id": CID}, "GET", f"/{CID}/"
)
self.assertEndpointReached(self.myob.companyfiles.get, {"id": CID}, "GET", f"/{CID}/")
# Don't expect companyfile credentials here as the next endpoint is not companyfile specific.
del self.expected_request_headers["x-myobapi-cftoken"]
self.assertEndpointReached(self.myob.companyfiles.all, {}, "GET", "/")
Expand Down Expand Up @@ -175,9 +169,7 @@ def test_banking(self):
" transfermoneytxn() - Return all transfer money transactions for an AccountRight company file."
),
)
self.assertEndpointReached(
self.companyfile.banking.all, {}, "GET", f"/{CID}/Banking/"
)
self.assertEndpointReached(self.companyfile.banking.all, {}, "GET", f"/{CID}/Banking/")
self.assertEndpointReached(
self.companyfile.banking.spendmoneytxn,
{},
Expand Down Expand Up @@ -292,9 +284,7 @@ def test_contacts(self):
" supplier() - Return all supplier contacts for an AccountRight company file."
),
)
self.assertEndpointReached(
self.companyfile.contacts.all, {}, "GET", f"/{CID}/Contact/"
)
self.assertEndpointReached(self.companyfile.contacts.all, {}, "GET", f"/{CID}/Contact/")
self.assertEndpointReached(
self.companyfile.contacts.customer, {}, "GET", f"/{CID}/Contact/Customer/"
)
Expand Down Expand Up @@ -579,9 +569,7 @@ def test_quotes(self):
" service() - Return all service type sale quotes for an AccountRight company file."
),
)
self.assertEndpointReached(
self.companyfile.quotes.all, {}, "GET", f"/{CID}/Sale/Quote/"
)
self.assertEndpointReached(self.companyfile.quotes.all, {}, "GET", f"/{CID}/Sale/Quote/")
self.assertEndpointReached(
self.companyfile.quotes.item, {}, "GET", f"/{CID}/Sale/Quote/Item/"
)
Expand Down Expand Up @@ -1298,9 +1286,7 @@ def test_timeout(self):
def test_exceptions(self):
self.assertExceptionHandled(400, {}, MyobBadRequest)
self.assertExceptionHandled(401, {}, MyobUnauthorized)
self.assertExceptionHandled(
403, {"Errors": [{"Name": "Something"}]}, MyobForbidden
)
self.assertExceptionHandled(403, {"Errors": [{"Name": "Something"}]}, MyobForbidden)
self.assertExceptionHandled(
403, {"Errors": [{"Name": "RateLimitError"}]}, MyobRateLimitExceeded
)
Expand Down
23 changes: 10 additions & 13 deletions tests/test_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,42 @@ class QueryParamTests(TestCase):
def setUp(self):
cred = PartnerCredentials(
consumer_key="KeyToTheKingdom",
consumer_secret="TellNoOne",
consumer_secret="TellNoOne", # noqa: S106
callback_uri="CallOnlyWhenCalledTo",
)
self.manager = Manager("", credentials=cred)

def assertParamsEqual(self, raw_kwargs, expected_params, method="GET"):
def assertParamsEqual(self, raw_kwargs, expected_params, method="GET"): # noqa: N802
self.assertEqual(
self.manager.build_request_kwargs(method, {}, **raw_kwargs)["params"],
expected_params,
)

def test_filter(self):
self.assertParamsEqual(
{"Type": "Customer"}, {"$filter": "(Type eq 'Customer')"}
)
self.assertParamsEqual({"Type": "Customer"}, {"$filter": "(Type eq 'Customer')"})
self.assertParamsEqual(
{"Type": ["Customer", "Supplier"]},
{"$filter": "(Type eq 'Customer' or Type eq 'Supplier')"},
)
self.assertParamsEqual(
{"DisplayID__gt": "5-0000"}, {"$filter": "(DisplayID gt '5-0000')"}
)
self.assertParamsEqual({"DisplayID__gt": "5-0000"}, {"$filter": "(DisplayID gt '5-0000')"})
self.assertParamsEqual(
{"DateOccurred__lt": "2013-08-30T19:00:59.043"},
{"$filter": "(DateOccurred lt '2013-08-30T19:00:59.043')"},
)
self.assertParamsEqual(
{"Type": ("Customer", "Supplier"), "DisplayID__gt": "5-0000"},
{
"$filter": "(Type eq 'Customer' or Type eq 'Supplier') and (DisplayID gt '5-0000')"
},
{"$filter": "(Type eq 'Customer' or Type eq 'Supplier') and (DisplayID gt '5-0000')"},
)
self.assertParamsEqual(
{
"raw_filter": "(Type eq 'Customer' or Type eq 'Supplier') or DisplayID gt '5-0000'",
"DateOccurred__lt": "2013-08-30T19:00:59.043",
},
{
"$filter": "((Type eq 'Customer' or Type eq 'Supplier') or DisplayID gt '5-0000') and (DateOccurred lt '2013-08-30T19:00:59.043')"
"$filter": (
"((Type eq 'Customer' or Type eq 'Supplier') or DisplayID gt '5-0000') "
"and (DateOccurred lt '2013-08-30T19:00:59.043')"
)
},
)
self.assertParamsEqual(
Expand Down Expand Up @@ -85,7 +82,7 @@ def test_templatename(self):
{"templatename": "InvoiceTemplate - 7"},
)

def test_returnBody(self):
def test_returnbody(self):
self.assertParamsEqual({}, {"returnBody": "true"}, method="PUT")
self.assertParamsEqual({}, {"returnBody": "true"}, method="POST")

Expand Down

0 comments on commit 74107a4

Please sign in to comment.