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

Add initial tests for exports and run them in mypy and pyright #1135

Merged
merged 9 commits into from
Nov 16, 2023
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ on:

jobs:
lint:
name: Lint
name: Lint & Mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: mypy
run: make mypy
- name: lint
run: make lint
- name: fmtcheck
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ $(VENV_NAME)/bin/activate: setup.py requirements.txt
${VENV_NAME}/bin/python -m pip install -r requirements.txt
@touch $(VENV_NAME)/bin/activate

test: venv pyright lint
test: venv pyright lint mypy
@${VENV_NAME}/bin/tox -p auto -e $(DEFAULT_TEST_ENV) $(TOX_ARGS)

test-nomock: venv
Expand All @@ -25,6 +25,9 @@ coveralls: venv
pyright: venv
@${VENV_NAME}/bin/tox -e pyright $(PYRIGHT_ARGS)

mypy: venv
@${VENV_NAME}/bin/tox -e mypy $(MYPY_ARGS)

fmt: venv
@${VENV_NAME}/bin/tox -e fmt

Expand All @@ -43,4 +46,4 @@ update-version:

codegen-format: fmt

.PHONY: ci-test clean codegen-format coveralls fmt fmtcheck lint test test-nomock test-travis update-version venv pyright
.PHONY: ci-test clean codegen-format coveralls fmt fmtcheck lint test test-nomock test-travis update-version venv pyright mypy
16 changes: 15 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,24 @@ exclude = '''
)
'''
[tool.pyright]
include = ["stripe", "tests/test_generated_examples.py"]
include = [
"stripe",
"tests/test_generated_examples.py",
"tests/test_exports.py",
]
exclude = ["build", "**/__pycache__"]
reportMissingTypeArgument = true
reportUnnecessaryCast = true
reportUnnecessaryComparison = true
reportUnnecessaryContains = true
reportUnnecessaryIsInstance = true
reportPrivateImportUsage = true
reportUnnecessaryTypeIgnoreComment = true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to use #type: ignore in import tests for negative cases.


[tool.mypy]
follow_imports = "silent"
python_version = "3.10"
files = ["tests/test_exports.py"]
disallow_untyped_calls = true
disallow_untyped_defs = true
warn_unused_ignores = true
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ virtualenv<20.22.0
pyright == 1.1.336
black == 22.8.0
flake8
mypy == 1.7.0

-r test-requirements.txt
4 changes: 2 additions & 2 deletions stripe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
log: Optional[Literal["debug", "info"]] = None

# API resources
from stripe.api_resources import * # pyright: ignore # noqa
from stripe.api_resources import * # noqa

from stripe.api_resources import abstract # pyright: ignore # noqa
from stripe.api_resources import abstract # noqa

# OAuth
from stripe.oauth import OAuth # noqa
Expand Down
2 changes: 1 addition & 1 deletion stripe/api_requestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def __init__(
self._client = client
elif stripe.default_http_client:
self._client = stripe.default_http_client
if proxy != self._default_proxy: # type: ignore
if proxy != self._default_proxy:
warnings.warn(
"stripe.proxy was updated after sending a "
"request - this is a no-op. To use a different proxy, "
Expand Down
4 changes: 2 additions & 2 deletions stripe/api_resources/abstract/api_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def _static_request(

if idempotency_key is not None:
headers = {} if headers is None else headers.copy()
headers.update(util.populate_headers(idempotency_key)) # type: ignore
headers.update(util.populate_headers(idempotency_key))

response, api_key = requestor.request(method_, url_, params, headers)
return util.convert_to_stripe_object(
Expand Down Expand Up @@ -186,7 +186,7 @@ def _static_request_stream(

if idempotency_key is not None:
headers = {} if headers is None else headers.copy()
headers.update(util.populate_headers(idempotency_key)) # type: ignore
headers.update(util.populate_headers(idempotency_key))

response, _ = requestor.request_stream(method_, url_, params, headers)
return response
2 changes: 1 addition & 1 deletion stripe/api_resources/abstract/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def class_url(cls):
)
# Namespaces are separated in object names with periods (.) and in URLs
# with forward slashes (/), so replace the former with the latter.
base = cls._resource_cls.OBJECT_NAME.replace(".", "/") # type: ignore
base = cls._resource_cls.OBJECT_NAME.replace(".", "/")
return "/v1/test_helpers/%ss" % (base,)

def instance_url(self):
Expand Down
12 changes: 8 additions & 4 deletions stripe/api_resources/list_object.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# pyright: strict
# pyright: strict, reportUnnecessaryTypeIgnoreComment=false
# reportUnnecessaryTypeIgnoreComment is set to false because some type ignores are required in some
# python versions but not the others
from typing_extensions import Self

from typing import (
Expand Down Expand Up @@ -115,7 +117,9 @@ def __getitem__(self, k: str) -> T:
# Pyright doesn't like this because ListObject inherits from StripeObject inherits from Dict[str, Any]
# and so it wants the type of __iter__ to agree with __iter__ from Dict[str, Any]
# But we are iterating through "data", which is a List[T].
def __iter__(self) -> Iterator[T]: # pyright: ignore
def __iter__( # pyright: ignore
self,
) -> Iterator[T]:
return getattr(self, "data", []).__iter__()

def __len__(self) -> int:
Expand All @@ -132,11 +136,11 @@ def auto_paging_iter(self) -> Iterator[T]:
"ending_before" in self._retrieve_params
and "starting_after" not in self._retrieve_params
):
for item in reversed(page): # type: ignore
for item in reversed(page):
yield item
page = page.previous_page()
else:
for item in page: # type: ignore
for item in page:
yield item
page = page.next_page()

Expand Down
2 changes: 1 addition & 1 deletion stripe/api_resources/quote.py
Original file line number Diff line number Diff line change
Expand Up @@ -1567,7 +1567,7 @@ def pdf(
...

@util.class_method_variant("_cls_pdf")
def pdf( # type: ignore
def pdf( # pyright: ignore
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
self,
api_key=None,
api_version=None,
Expand Down
2 changes: 1 addition & 1 deletion stripe/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def __repr__(self):
% (
self.__class__.__name__,
self._message,
self.param, # type: ignore
self.param, # pyright: ignore
self.code,
self.http_status,
self.request_id,
Expand Down
4 changes: 2 additions & 2 deletions stripe/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
pycurl = None

try:
import requests # pyright: ignore
import requests
except ImportError:
requests = None
else:
Expand Down Expand Up @@ -61,7 +61,7 @@
requests = None

try:
from google.appengine.api import urlfetch # type: ignore
from google.appengine.api import urlfetch # pyright: ignore
except ImportError:
urlfetch = None

Expand Down
5 changes: 4 additions & 1 deletion stripe/multipart_data_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ def __init__(self, chunk_size: int = 1028):

def add_params(self, params):
# Flatten parameters first
params = dict(stripe.api_requestor._api_encode(params)) # type: ignore

params = dict(
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
stripe.api_requestor._api_encode(params) # pyright: ignore
)

for key, value in params.items():
if value is None:
Expand Down
2 changes: 1 addition & 1 deletion stripe/oauth_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def construct_error_object(self):
if self.json_body is None:
return None

return stripe.api_resources.error_object.OAuthErrorObject.construct_from( # type: ignore
return stripe.api_resources.error_object.OAuthErrorObject.construct_from( # pyright: ignore
self.json_body, stripe.api_key
)

Expand Down
4 changes: 3 additions & 1 deletion stripe/stripe_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ def last_response(self) -> Optional[StripeResponse]:

# StripeObject inherits from `dict` which has an update method, and this doesn't quite match
# the full signature of the update method in MutableMapping. But we ignore.
def update(self, update_dict: Mapping[str, Any]) -> None: # type: ignore[override]
def update( # pyright: ignore
self, update_dict: Mapping[str, Any]
) -> None:
for k in update_dict:
self._unsaved_values.add(k)

Expand Down
98 changes: 98 additions & 0 deletions tests/test_exports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# pyright: strict
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
import stripe


def test_can_import_stripe_object() -> None:
from stripe.stripe_object import (
StripeObject as StripeObjectFromStripeStripeObject,
)

assert (
stripe.stripe_object.StripeObject is StripeObjectFromStripeStripeObject
)


def test_can_import_webhook_members() -> None:
from stripe import (
Webhook,
WebhookSignature,
)

assert Webhook is not None
assert WebhookSignature is not None


def test_can_import_abstract() -> None:
from stripe.api_resources.abstract import (
APIResource as APIResourceFromApiResourcesAbstract,
)
from stripe.stripe_object import (
StripeObject,
)

assert (
APIResourceFromApiResourcesAbstract[StripeObject]
== stripe.abstract.APIResource[StripeObject]
)


def test_can_import_app_info() -> None:
from stripe.app_info import AppInfo as AppInfoFromStripeAppInfo
from stripe import AppInfo as AppInfoFromStripe

assert AppInfoFromStripeAppInfo is AppInfoFromStripe
assert AppInfoFromStripeAppInfo is stripe.AppInfo


def test_can_import_stripe_response() -> None:
from stripe.stripe_response import (
StripeResponse as StripeResponseFromStripeResponse,
)

assert (
StripeResponseFromStripeResponse
is stripe.stripe_response.StripeResponse
)


def test_can_import_oauth_members() -> None:
from stripe import (
OAuth,
)

assert OAuth is not None


def test_can_import_errors() -> None:
from stripe.error import (
StripeError as StripeErrorFromStripeError,
)

assert StripeErrorFromStripeError is not None


def test_can_import_top_level_resource() -> None:
from stripe import Account as AccountFromStripe
from stripe.api_resources import Account as AccountFromStripeResources
from stripe.api_resources.account import (
Account as AccountFromStripeResourcesAccount,
)

assert stripe.Account == AccountFromStripe
assert AccountFromStripe == AccountFromStripeResources
assert AccountFromStripeResourcesAccount == AccountFromStripeResources


def test_can_import_namespaced_resource() -> None:
from stripe import tax as TaxPackage
from stripe.api_resources.tax import (
Calculation as CalculationFromResources,
)
from stripe.api_resources.tax.calculation import (
Calculation as CalculationFromResourcesCalculation,
)

assert stripe.tax is TaxPackage
assert stripe.tax.Calculation is TaxPackage.Calculation
assert stripe.tax.Calculation is CalculationFromResources
assert CalculationFromResources is CalculationFromResourcesCalculation
4 changes: 3 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ envlist =
fmt
lint
pyright
mypy
py{312,311,310,39,38,37,36,py3}
ignore_base_python_conflict = false

Expand All @@ -32,13 +33,14 @@ commands = pytest --cov {posargs:-n auto} --ignore stripe
# CFLAGS="-I$(brew --prefix openssl@1.1)/include"
passenv = LDFLAGS,CFLAGS

[testenv:{lint,fmt,pyright}]
[testenv:{lint,fmt,pyright,mypy}]
basepython = python3.10
skip_install = true
commands =
pyright: pyright {posargs}
lint: python -m flake8 --show-source stripe tests setup.py
fmt: black . {posargs}
mypy: mypy {posargs}
deps =
-r requirements.txt

Expand Down