Skip to content

Commit

Permalink
Merge pull request #3 from vr2262/type-hints
Browse files Browse the repository at this point in the history
Add type hints
  • Loading branch information
vr2262 authored Jan 23, 2021
2 parents 9b44ce1 + 5380925 commit b7d42ad
Show file tree
Hide file tree
Showing 22 changed files with 633 additions and 330 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/docstyle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Check documentation style

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install pydocstyle
- name: Lint docs
run: |
pydocstyle
17 changes: 17 additions & 0 deletions .github/workflows/isort.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Check import sort

on:
- push

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
pip install -e .
- uses: isort/isort-action@master
24 changes: 24 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Python lint

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install flake8-bugbear
- name: Lint with flake8
run: |
flake8
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Python tests and coverage

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -e .
pip install -r requirements-dev.txt
- name: Run tests with coverage
run: |
coverage run --source=asyncio_portier --branch -m pytest
coverage report -m --fail-under=100
25 changes: 25 additions & 0 deletions .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Static type checking

on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -e .
pip install -r requirements-dev.txt
- name: Check types with mypy
run: |
mypy
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include LICENSE
include asyncio_portier/py.typed
10 changes: 2 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
Portier authentication Python helpers that are asyncio-aware
============================================================

|travis| |master-coverage|
|coverage|

.. |travis| image:: https://travis-ci.org/vr2262/asyncio-portier.svg?branch=master
:target: https://travis-ci.org/vr2262/asyncio-portier

.. |master-coverage| image::
https://codecov.io/gh/vr2262/asyncio-portier/branch/master/graph/badge.svg
:alt: Coverage
:target: https://codecov.io/gh/vr2262/asyncio-portier
.. |coverage| image:: https://github.com/vr2262/asyncio-portier/workflows/Python%20tests%20and%20coverage/badge.svg

*asyncio-portier* is a set of helpers for `the Portier Identity Provider
<https://portier.github.io/>`_. It is based on `the portier-python package
Expand Down
2 changes: 1 addition & 1 deletion asyncio_portier/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Public API definitions for asyncio-portier."""
import pkg_resources
from .client import discover_keys, get_verified_email

from .client import discover_keys, get_verified_email

# Module version, as defined in PEP-0396.
__version__ = pkg_resources.get_distribution('asyncio-portier').version
Expand Down
91 changes: 91 additions & 0 deletions asyncio_portier/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""Cache utilities.
This tries to smooth out type-hinting and signature variations among caches.
For the moment, that means redis and aioredis.
"""
from collections.abc import Awaitable
from datetime import timedelta
from inspect import isawaitable
from typing import Optional, Protocol, Union, cast


class BlockingCache(Protocol): # pragma: no cover
"""A protocol for the more-or-less redis cache object we need."""

def get(self, name: Union[str, bytes]) -> Union[None, str, bytes]:
"""Retrieve a value from the cache by name."""

def set(
self,
name: Union[str, bytes],
value: Union[bytes, float, int, str],
ex: Union[None, int, timedelta] = ...
) -> Optional[bool]:
"""Set a value by name with optional expiration."""

def delete(self, *names: Union[str, bytes]) -> int:
"""Delete values from the cache by name."""


class AsyncCache(Protocol): # pragma: no cover
"""A protocol for the more-or-less aioredis cache object we need."""

def get(
self, name: Union[str, bytes]
) -> Awaitable[Union[None, str, bytes]]:
"""Retrieve an awaitable value from the cache by name."""

def set(
self,
name: Union[str, bytes],
value: Union[bytes, float, int, str],
expire: int = ... # why does aioredis do this???
) -> Awaitable[Optional[bool]]:
"""Set a value by name (expire is a kwarg?), returning an awaitable."""

async def delete(self, *names: Union[str, bytes]) -> Awaitable[int]:
"""Delete values from the cache by name, returning an awaitable."""


GenericCache = Union[BlockingCache, AsyncCache]


async def cache_get(
cache: GenericCache, name: Union[str, bytes]
) -> Optional[str]:
"""Retrieve a value from the cache by name."""
if isawaitable(_result := cache.get(name)):
result = await cast(Awaitable[Union[None, str, bytes]], _result)
else:
result = cast(Union[None, str, bytes], _result)

return result.decode() if isinstance(result, bytes) else result


async def cache_set(
cache: GenericCache,
name: Union[str, bytes],
value: Union[bytes, float, int, str],
expiration: Union[None, int, timedelta] = timedelta(minutes=5).seconds,
) -> Optional[bool]:
"""Set a value by name with default 5 minute expiration."""
try:
return cast(BlockingCache, cache).set(name, value, expiration)
except TypeError:
pass

if expiration is None:
expire = 0
elif isinstance(expiration, timedelta):
expire = expiration.seconds
else:
expire = expiration

return await cast(AsyncCache, cache).set(name, value, expire=expire)


async def cache_delete(cache: GenericCache, name: Union[str, bytes]) -> int:
"""Delete a value from the cache by name."""
if isawaitable(result := cache.delete(name)):
return await cast(Awaitable[int], result)
return cast(int, result)
Loading

0 comments on commit b7d42ad

Please sign in to comment.