Skip to content

Commit

Permalink
Add tests for mypy plugin with pytest-mypy-plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
sanjacob committed Jan 6, 2024
1 parent e9c8f0c commit 2bbbc70
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
run: mypy --strict tiny_api_client

- name: Run test suite
run: pytest -vvvv
run: pytest --mypy-ini-file=tests/test_mypy_plugin.ini --mypy-only-local-stub -v

pypi:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ flake8 = "*"
flake8-pyproject = "*"
pre-commit = "*"
twine = "*"
pytest-mypy-plugins = "*"

[requires]
python_version = "3.10"
137 changes: 130 additions & 7 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

[![License: GPL v2][license-shield]][gnu]

Write JSON API Clients in Python without the fluff, pumped full of syntactic sugar
Write JSON API Clients in Python without the fluff, pumped full of
syntactic sugar

```python
from tiny_api_client import api_client, get, post, delete
Expand All @@ -24,7 +25,7 @@ class MyAPIClient:
>>> client = MyClient()
>>> client.find_user(user_id='PeterParker')
{'name': 'Peter', 'surname': 'Parker', ...}
>>> client.create_note(data={'title': 'My New Note', 'content': 'Hello World!'})
>>> client.create_note(data={'title': 'New Note', 'content': 'Hello World!'})
{'id': ...}
>>> client.delete_note_attachment(node_id=...)
```
Expand All @@ -33,7 +34,8 @@ class MyAPIClient:

## Features

- Instance-scoped `requests.Session()` with connection pooling and cookie preservation
- Instance-scoped `requests.Session()` with connection pooling and
cookie preservation
- JSON is king, but XML and raw responses are fine too
- Endpoints can use GET, POST, PUT, PATCH, DELETE
- Route parameters are optional
Expand All @@ -44,6 +46,7 @@ class MyAPIClient:
- Pass along any parameters you would usually pass to requests
- Custom JSON status error handling
- Installable [pytest plugin][pytest-plugin] for easy testing
- Excellent support for type checking thanks to a built-in mypy plugin



Expand All @@ -57,15 +60,18 @@ pip install tiny-api-client

## Documentation

You can find the documentation at https://tiny-api-client.readthedocs.io
You can find the documentation at
https://tiny-api-client.readthedocs.io



## License

[![License: LGPL v2.1][license-shield]][gnu]

This software is distributed under the [Lesser General Public License v2.1][license], more information available at the [Free Software Foundation][gnu].
This software is distributed under the
[Lesser General Public License v2.1][license],
more information available at the [Free Software Foundation][gnu].



Expand Down
2 changes: 2 additions & 0 deletions tests/test_mypy_plugin.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mypy]
plugins = tiny_api_client.mypy
64 changes: 64 additions & 0 deletions tests/test_mypy_plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
- case: mypy_plugin_correct_route_param
main: |
from tiny_api_client import get, api_client
@api_client('https://api.example.org')
class MyClient:
@get('/users/{user_id}')
def get_users(self, response: list[str]) -> list[str]:
return response
client = MyClient()
client.get_users(user_id='peterparker')
env:
- PYTHONPATH=$(pwd)/../

- case: mypy_plugin_optional_route_param
main: |
from tiny_api_client import get, api_client
@api_client('https://api.example.org')
class MyClient:
@get('/users/{user_id}')
def get_users(self, response: list[str]) -> list[str]:
return response
client = MyClient()
client.get_users()
env:
- PYTHONPATH=$(pwd)/../

- case: mypy_plugin_wrong_route_param
main: |
from tiny_api_client import get, api_client
@api_client('https://api.example.org')
class MyClient:
@get('/users/{user_id}')
def get_users(self, response: list[str]) -> list[str]:
return response
client = MyClient()
client.get_users(unknown_id='idk')
env:
- PYTHONPATH=$(pwd)/../
out: |
main:10: error: Unexpected keyword argument "unknown_id" for "get_users" of "MyClient" [call-arg]
- case: mypy_plugin_wrong_extra_route_param
main: |
from tiny_api_client import get, api_client
@api_client('https://api.example.org')
class MyClient:
@get('/users/{user_id}')
def get_users(self, response: list[str]) -> list[str]:
return response
client = MyClient()
client.get_users(user_id='peterparker', unknown_id='idk')
env:
- PYTHONPATH=$(pwd)/../
out: |
main:10: error: Unexpected keyword argument "unknown_id" for "get_users" of "MyClient" [call-arg]
1 change: 1 addition & 0 deletions tiny_api_client/mypy.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def _decorator_callback(self, ctx: MethodContext) -> Type:
"""
pos = f"{ctx.context.line},{ctx.context.column}"
default_ret = ctx.default_return_type
# need this to access properties without a warning
assert isinstance(default_ret, CallableType)

# Modify default return type in place (probably fine)
Expand Down

0 comments on commit 2bbbc70

Please sign in to comment.