Skip to content

Commit

Permalink
Typechecked annotations (and droped support for python 3.5)
Browse files Browse the repository at this point in the history
Also adds mypy to enforce typing
  • Loading branch information
graingert committed Feb 4, 2021
1 parent 2b6174c commit d35966b
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 27 deletions.
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- 3.5
- 3.6
- 3.7
- 3.8
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ file handles for incoming POST bodies).
Dependencies
------------

``asgiref`` requires Python 3.5 or higher.
``asgiref`` requires Python 3.6 or higher.


Contributing
Expand Down
4 changes: 2 additions & 2 deletions asgiref/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class Local:

CLEANUP_INTERVAL = 60 # seconds

def __init__(self, thread_critical=False):
def __init__(self, thread_critical: bool = False) -> None:
self._thread_critical = thread_critical
self._thread_lock = threading.RLock()
self._context_refs = weakref.WeakSet()
self._context_refs: "weakref.WeakSet[object]" = weakref.WeakSet()
# Random suffixes stop accidental reuse between different Locals,
# though we try to force deletion as well.
self._attr_name = "_asgiref_local_impl_%s_%s" % (
Expand Down
16 changes: 9 additions & 7 deletions asgiref/sync.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import asyncio
import asyncio.coroutines
import functools
import os
import sys
import threading
import weakref
from concurrent.futures import Future, ThreadPoolExecutor
from typing import Dict

from .current_thread_executor import CurrentThreadExecutor
from .local import Local

try:
import contextvars # Python 3.7+ only.
except ImportError:
if sys.version_info >= (3, 7):
import contextvars
else:
contextvars = None


Expand Down Expand Up @@ -94,7 +94,7 @@ class AsyncToSync:
"""

# Maps launched Tasks to the threads that launched them (for locals impl)
launch_map = {}
launch_map: "Dict[asyncio.Task[object], threading.Thread]" = {}

# Keeps track of which CurrentThreadExecutor to use. This uses an asgiref
# Local, not a threadlocal, so that tasks can work out what their parent used.
Expand Down Expand Up @@ -301,7 +301,7 @@ class SyncToAsync:
)

# Maps launched threads to the coroutines that spawned them
launch_map = {}
launch_map: "Dict[threading.Thread, asyncio.Task[object]]" = {}

# Storage for main event loop references
threadlocal = threading.local()
Expand All @@ -317,7 +317,9 @@ class SyncToAsync:

# Maintaining a weak reference to the context ensures that thread pools are
# erased once the context goes out of scope. This terminates the thread pool.
context_to_thread_executor = weakref.WeakKeyDictionary()
context_to_thread_executor: "weakref.WeakKeyDictionary[object, ThreadPoolExecutor]" = (
weakref.WeakKeyDictionary()
)

def __init__(self, func, thread_sensitive=True):
self.func = func
Expand Down
15 changes: 7 additions & 8 deletions asgiref/timeout.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
import asyncio
import sys
from types import TracebackType
from typing import Any, Optional, Type # noqa

PY_37 = sys.version_info >= (3, 7)
from typing import Any, Optional, Type


class timeout:
Expand Down Expand Up @@ -115,14 +113,15 @@ def _cancel_task(self) -> None:
self._cancelled = True


def current_task(loop: asyncio.AbstractEventLoop) -> "asyncio.Task[Any]":
if PY_37:
task = asyncio.current_task(loop=loop) # type: ignore
def current_task(loop: asyncio.AbstractEventLoop) -> "Optional[asyncio.Task[Any]]":
if sys.version_info >= (3, 7):
task = asyncio.current_task(loop=loop)
else:
task = asyncio.Task.current_task(loop=loop)
if task is None:
# this should be removed, tokio must use register_task and family API
if hasattr(loop, "current_task"):
task = loop.current_task() # type: ignore
fn = getattr(loop, "current_task", None)
if fn is not None:
task = fn()

return task
7 changes: 4 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import List, Dict
#
# ASGI documentation build configuration file, created by
# sphinx-quickstart on Thu May 17 21:22:10 2018.
Expand Down Expand Up @@ -31,10 +32,10 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions: List[str] = []

# Add any paths that contain templates here, relative to this directory.
templates_path = []
templates_path: List[str] = []

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
Expand Down Expand Up @@ -120,7 +121,7 @@

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
latex_elements: Dict[str, str] = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
Expand Down
64 changes: 62 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ classifiers =
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Expand All @@ -28,7 +27,7 @@ project_urls =
Changelog = https://github.com/django/asgiref/blob/master/CHANGELOG.txt

[options]
python_requires = >=3.5
python_requires = >=3.6
packages = find:
include_package_data = true
install_requires =
Expand All @@ -39,6 +38,7 @@ zip_safe = false
tests =
pytest
pytest-asyncio
mypy>=0.800

[tool:pytest]
testpaths = tests
Expand All @@ -50,3 +50,63 @@ max-line-length = 119

[isort]
line_length = 119

[mypy]
warn_unused_ignores = True
strict = True

[mypy-asgiref.current_thread_executor]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.local]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.sync]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.compatibility]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.wsgi]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.testing]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-asgiref.server]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_server]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_wsgi]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_testing]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_sync_contextvars]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_sync]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_local]
disallow_untyped_defs = False
check_untyped_defs = False

[mypy-test_compatibility]
disallow_untyped_defs = False
check_untyped_defs = False
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from setuptools import setup
from setuptools import setup # type: ignore[import]

setup(name='asgiref')
5 changes: 3 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[tox]
envlist =
py{35,36,37,38,39}
py{36,37,38,39}-{test,mypy}
qa

[testenv]
usedevelop = true
extras = tests
commands =
pytest -v {posargs}
test: pytest -v {posargs}
mypy: mypy . {posargs}

[testenv:qa]
skip_install=true
Expand Down

0 comments on commit d35966b

Please sign in to comment.