Skip to content

Commit

Permalink
Add support for pytypes.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kentzo committed Jan 9, 2018
1 parent dd67de1 commit 58689c1
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 48 deletions.
7 changes: 3 additions & 4 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ environment:

install:
- "%PYTHON%\\python.exe -m pip install --upgrade pip"
- "%PYTHON%\\python.exe -m pip install --upgrade wheel setuptools>=37"
- "%PYTHON%\\python.exe -m pip install \".[ci]\" -c requirements.txt"
- "%PYTHON%\\python.exe -m pip install --upgrade wheel setuptools"
- "%PYTHON%\\python.exe -m pip install --upgrade tox"

build: off

test_script:
- "%PYTHON%\\python.exe setup.py test"
- "%PYTHON%\\python.exe setup.py check -srm"
- "%PYTHON%\\python.exe -m tox"
19 changes: 11 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ matrix:
- os: linux
language: python
python: 3.6
script:
- python setup.py test --pytest-args "--cov=async_app --cov-report=html --cov-branch --ignore=setup.py"
- python setup.py check -srm
env:
- PYTEST_ADDOPTS="--cov=async_app --cov-branch --ignore=setup.py"
- COVERAGE_FILE_PYTYPES="/tmp/.coverage_pytypes"
- COVERAGE_FILE_TYPEGUARD="/tmp/.coverage_typeguard"
after_success:
- flake8 async_app
- coverage combine ${COVERAGE_FILE_PYTYPES} ${COVERAGE_FILE_TYPEGUARD}
- coverage html
- codecov
- os: linux
language: python
Expand All @@ -42,19 +45,19 @@ matrix:

install:
- python -m pip install --upgrade pip
- python -m pip install ".[ci]" -c requirements.txt
- python -m pip install --upgrade wheel setuptools
- python -m pip install --upgrade tox

script:
- python setup.py test
- python setup.py check -srm
- python -m tox

deploy:
provider: pypi
user: Ilya.Kulakov
password:
secure: "ZhPxrdihJ+oK+3n2ApnOpGDIWJF5PUZYbfO50d8HuROQWQtuOYaZfV7hbUmo8O8WasT2ih6RC8VWaZ6B80eEmAm0o/gW2lA8JUbwiPL216psdmMLevwNv1ta49nFFZ/OfLZXfkkKoXPr7DQaShFi6aD7Y9/r3Dk0LVMvmWQR8oqOQErkB6Rp819JFmuAytZplfzGltKa/r4/oNLU8aZe4qsPGciWC8u8H38eiaqHt7gQBXmuRa3d9Nd85L/Q8rmJM5YjOrV9Ssf2OTq6ICavQ6YUaErrhcqrz6ggrHDOAkdwZilgO0/5WYL/NTQ/X6f2wvdDVF2GPiYDdHSES1As8kIfMn+A7Z4KJnqAh3mo6R4ILf9SPnspvNMTnsj+uHwW+eX5HjTobLU9zV8gUfjbSHTgSazbydy5FMYULPkEMPfWng+7gvMramBAoX7Fofql9gAfoMX1SuJAlNnzF3n4tQDbk5tlepmXEcGGgUhJS3Qhwms4FC4CDDqSZNxOwdibBZCOhQ7PWqHxHYosthx7SwYN8CRhwK/rC2XJINkTQWWfEst0+ODHuf0VBFVzlc7Tllj0mAZHEgEK37ZYNq8OJgMN5DeN+AqpHNqs41Kol8oiRMLAEXTHNaD0gzrDrGl39hzZRqmzKkSOtmycb/dCmW0ZVPJ/moZAz90Sr6Pd2N8="
distributions: "sdist bdist_wheel"
distributions: sdist bdist_wheel
on:
tags: true
branch: master
python: 3.6
python: 3.6
16 changes: 10 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@
Key Features
============

- Ideal for heterogeneous asyncio applications
- `typing-friendly <https://docs.python.org/3/library/typing.html>`_ Config that enforces neither file format nor validation
- Service-oriented application layout
- Integrate different asyncio libraries with ease
- `typing-friendly <https://docs.python.org/3/library/typing.html>`_ Config that can enforce types (via `typeguard <typeguard>`_ or `pytypes <pytypes>`_)


Development
============
===========

requirements.txt lists all dependencies needed to run tests and generate reports. See `.travis.yml <.travis.yml>`_ for commands.
requirements.txt lists all dependencies needed to run tests and generate reports.

CI tests each change against latest release of CPython 3 (Windows and macOS) as well as dev (macOS and Ubuntu) and nightly builds (Ubuntu).
PyPy will be added as soon as all necessary 3.6 features will be implemented.
CI tests each change against latest release of CPython 3 (Windows and macOS) as well as dev (macOS and Ubuntu)
and nightly builds (Ubuntu).
Tests are run against both pytypes and typeguard. Combined coverage is uploaded to PyPI.
See `.travis.yml <.travis.yml>`_, `.appveyor.yml <.appveyor.yml>`_ and `setup.cfg <setup.cfg>`_
for the detailed configuration.
55 changes: 53 additions & 2 deletions async_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,48 @@
"""
from collections import ChainMap, UserDict, OrderedDict
import copy
import inspect
import logging
from typing import Any, Callable, ClassVar, Dict, Generic, GenericMeta, Iterable, Hashable, Optional, Type, TypeVar, Union, get_type_hints

import typeguard
try:
import typeguard
except ImportError:
_HAS_TYPEGUARD = False
else:
_HAS_TYPEGUARD = True

try:
import pytypes
except ImportError:
_HAS_PYTYPES = False
else:
_HAS_PYTYPES = True


LOG = logging.getLogger(__name__)

if not _HAS_TYPEGUARD and not _HAS_PYTYPES:
LOG.warning("Neither typeguard nor pytypes is installed. Type checking is disabled.")


Self = TypeVar('Self')
OptionType = TypeVar('OptionType')


def _qualified_name(obj) -> str:
"""
Return the qualified name (e.g. package.module.Type) for the given object.
Builtins and types from the :mod:`typing` package get special treatment by having the module
name stripped from the generated name.
"""
type_ = obj if inspect.isclass(obj) else type(obj)
module = type_.__module__
qualname = type_.__qualname__
return qualname if module in ('typing', 'builtins') else '{}.{}'.format(module, qualname)


class Option(Generic[OptionType]):
"""
Option is a named attribute with optional default value that can be type checked.
Expand Down Expand Up @@ -124,6 +157,8 @@ def resolve_value(self, instance: 'Config', value: OptionType):
Subclasses can override to transform value.
>>> from pathlib import Path
>>>
>>> class PathOption(Option[Path]):
>>> '''Transform str to Path'''
>>> def resolve_value(self, instance, value):
Expand Down Expand Up @@ -228,6 +263,9 @@ def check_type(cls, name: str, value: OptionType, *, attr_name: str = None, expe
@note: If name does not point to an existing option, typing.Any is implied.
"""
if not _HAS_TYPEGUARD and not _HAS_PYTYPES:
return

if attr_name is None and expected_type is None:
attr_name = cls._option_names.get(name)

Expand All @@ -237,7 +275,20 @@ def check_type(cls, name: str, value: OptionType, *, attr_name: str = None, expe
if expected_type is None:
expected_type = cls._option_types.get(attr_name, Any)

typeguard.check_type(name, value, expected_type, None)
if _HAS_TYPEGUARD:
try:
typeguard.check_type(name, value, expected_type, None)
except TypeError:
valid_type = False
else:
valid_type = True
elif _HAS_PYTYPES:
valid_type = pytypes.is_of_type(value, expected_type)

if not valid_type:
raise TypeError('type of {} must be {}; got {} instead'.format(name,
_qualified_name(expected_type),
_qualified_name(value)))

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ docutils==0.14
flake8==3.5.0
pytest==3.3.2
pytest-cov==2.5.1
pytypes==1.0b3
typeguard==2.1.4
42 changes: 34 additions & 8 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ url = https://github.com/Kentzo/async_app/
author = Ilya Kulakov
author_email = kulakov.ilya@gmail.com
classifiers =
Development Status :: 4 - Beta
Framework :: AsyncIO
Intended Audience :: Developers
Programming Language :: Python :: 3.6
Topic :: Software Development :: Libraries :: Application Frameworks
License :: OSI Approved :: Apache Software License
Development Status :: 4 - Beta
Framework :: AsyncIO
Intended Audience :: Developers
Programming Language :: Python :: 3.6
Topic :: Software Development :: Libraries :: Application Frameworks
License :: OSI Approved :: Apache Software License
license = Apache 2
description = AsyncIO application as a hierarchy of services
long_description = file: README.rst
Expand All @@ -21,10 +21,36 @@ python_requires = >=3.6
zip_safe = True

[bdist_wheel]
universal=1
universal = 1

[tool:pytest]
testpaths=tests
testpaths = tests

[flake8]
max-line-length = 120

[tox]
envlist =
typeguard
pytypes

[testenv]
deps = -crequirements.txt
commands =
python setup.py check -srm
pytest
passenv = PYTEST_ADDOPTS

[testenv:typeguard]
extras =
ci
typeguard
setenv =
COVERAGE_FILE = {env:COVERAGE_FILE_TYPEGUARD}

[testenv:pytypes]
extras =
ci
pytypes
setenv =
COVERAGE_FILE = {env:COVERAGE_FILE_PYTYPES}
7 changes: 3 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ def run_tests(self):


REQUIREMENTS = [
'typeguard'
]


TEST_REQUIREMENTS = [
'asynctest',
'pytest',
Expand All @@ -51,7 +49,8 @@ def run_tests(self):
install_requires=REQUIREMENTS,
tests_require=TEST_REQUIREMENTS,
extras_require={
'tests': TEST_REQUIREMENTS,
'ci': CI_REQUIREMENTS
'ci': CI_REQUIREMENTS,
'typeguard': ['typeguard'],
'pytypes': ['pytypes']
}
)

0 comments on commit 58689c1

Please sign in to comment.