Use nox for automation and CI (#128)

This commit is contained in:
Seth Michael Larson 2019-08-13 08:39:35 -05:00 committed by GitHub
parent 95dad247b5
commit 80dc3e274e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 116 additions and 78 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ __pycache__/
htmlcov/
*.egg-info/
venv/
.nox

3
.isort.cfg Normal file
View File

@ -0,0 +1,3 @@
[settings]
known_first_party = httpx
known_third_party = brotli,certifi,chardet,cryptography,h11,h2,hstspreload,nox,pytest,rfc3986,setuptools,trustme,uvicorn

View File

@ -4,16 +4,35 @@ language: python
cache: pip
python:
- "3.6"
- "3.7"
branches:
only:
- master
matrix:
include:
- python: 3.7
env: NOX_SESSION=check
- python: 3.7
env: NOX_SESSION=docs
- python: 3.6
env: NOX_SESSION=test-3.6
- python: 3.7
env: NOX_SESSION=test-3.7
- python: 3.8-dev
env: NOX_SESSION=test-3.8
allow_failures:
- python: 3.8-dev
install:
- pip install -U -r requirements.txt
- pip install --upgrade nox
script:
- scripts/test
- nox -s ${NOX_SESSION}
after_script:
- pip install codecov
- codecov
- if [ -f .coverage ]; then
python -m pip install codecov;
codecov;
fi

View File

@ -86,6 +86,8 @@ __all__ = [
"TimeoutConfig",
"VerifyTypes",
"HTTPConnection",
"BasePoolSemaphore",
"BaseBackgroundManager",
"ConnectionPool",
"ConnectTimeout",
"CookieConflict",

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
import typing
if TYPE_CHECKING:
if typing.TYPE_CHECKING:
from .models import BaseRequest, BaseResponse # pragma: nocover
@ -10,7 +10,10 @@ class HTTPError(Exception):
"""
def __init__(
self, request: "BaseRequest" = None, response: "BaseResponse" = None, *args
self,
*args: typing.Any,
request: "BaseRequest" = None,
response: "BaseResponse" = None,
) -> None:
self.response = response
self.request = request or getattr(self.response, "request", None)

View File

@ -41,7 +41,7 @@ class AsyncDispatcher:
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None
timeout: TimeoutTypes = None,
) -> AsyncResponse:
request = AsyncRequest(method, url, data=data, params=params, headers=headers)
return await self.send(request, verify=verify, cert=cert, timeout=timeout)
@ -89,7 +89,7 @@ class Dispatcher:
headers: HeaderTypes = None,
verify: VerifyTypes = None,
cert: CertTypes = None,
timeout: TimeoutTypes = None
timeout: TimeoutTypes = None,
) -> Response:
request = Request(method, url, data=data, params=params, headers=headers)
return self.send(request, verify=verify, cert=cert, timeout=timeout)

View File

@ -36,7 +36,7 @@ from .utils import (
str_query_param,
)
PrimitiveData = typing.Union[str, int, float, bool, type(None)]
PrimitiveData = typing.Optional[typing.Union[str, int, float, bool]]
URLTypes = typing.Union["URL", str]
@ -92,6 +92,16 @@ class URL:
else:
self._uri_reference = url._uri_reference
# Handle IDNA domain names.
if self._uri_reference.authority:
idna_authority = self._uri_reference.authority.encode("idna").decode(
"ascii"
)
if idna_authority != self._uri_reference.authority:
self._uri_reference = self._uri_reference.copy_with(
authority=idna_authority
)
# Normalize scheme and domain name.
if self.is_absolute_url:
self._uri_reference = self._uri_reference.normalize()
@ -170,7 +180,7 @@ class URL:
# URLs with a fragment portion as not absolute.
# What we actually care about is if the URL provides
# a scheme and hostname to which connections should be made.
return self.scheme and self.host
return bool(self.scheme and self.host)
@property
def is_relative_url(self) -> bool:
@ -252,7 +262,7 @@ class QueryParams(typing.Mapping[str, str]):
elif isinstance(value, QueryParams):
items = value.multi_items()
elif isinstance(value, list):
items = value
items = value # type: ignore
else:
items = value.items() # type: ignore

View File

@ -23,7 +23,7 @@ def normalize_header_value(value: typing.AnyStr, encoding: str = None) -> bytes:
return value.encode(encoding or "ascii")
def str_query_param(value: typing.Union[str, int, float, bool, type(None)]) -> str:
def str_query_param(value: typing.Optional[typing.Union[str, int, float, bool]]) -> str:
"""
Coerce a primitive data type into a string value for query params.
@ -100,5 +100,5 @@ def get_netrc_login(host: str) -> typing.Optional[typing.Tuple[str, str, str]]:
if netrc_path is None:
return None
netrc_info = netrc.netrc(netrc_path)
netrc_info = netrc.netrc(str(netrc_path))
return netrc_info.authenticators(host) # type: ignore

60
noxfile.py Normal file
View File

@ -0,0 +1,60 @@
import nox
source_files = ("httpx", "tests", "setup.py", "noxfile.py")
@nox.session(reuse_venv=True)
def lint(session):
session.install("autoflake", "black", "flake8", "isort", "seed-isort-config")
session.run("autoflake", "--in-place", "--recursive", *source_files)
session.run("seed-isort-config", "--application-directories=httpx")
session.run(
"isort",
"--project=httpx",
"--multi-line=3",
"--trailing-comma",
"--force-grid-wrap=0",
"--combine-as",
"--line-width=88",
"--recursive",
"--apply",
*source_files,
)
session.run("black", "--target-version=py36", *source_files)
check(session)
@nox.session(reuse_venv=True)
def check(session):
session.install("black", "flake8", "flake8-bugbear", "mypy")
session.run("black", "--check", "--target-version=py36", *source_files)
session.run(
"flake8", "--max-line-length=88", "--ignore=W503,E203,B305", *source_files
)
session.run("mypy", "httpx", "--ignore-missing-imports", "--disallow-untyped-defs")
@nox.session(reuse_venv=True)
def docs(session):
session.install("mkdocs", "mkdocs-material")
session.run("mkdocs", "build")
@nox.session(python=["3.6", "3.7", "3.8"])
def test(session):
session.install("-r", "test-requirements.txt")
session.run(
"coverage",
"run",
"--omit='*'",
"-m",
"pytest",
"--cov=httpx",
"--cov=tests",
"--cov-report=term-missing",
)

View File

@ -1,24 +0,0 @@
#!/bin/sh -e
# Use the Python executable provided from the `-p` option, or a default.
[[ $1 = "-p" ]] && PYTHON=$2 || PYTHON="python3"
MIN_VERSION="(3, 6)"
VERSION_OK=`"$PYTHON" -c "import sys; print(sys.version_info[0:2] >= $MIN_VERSION and '1' or '');"`
if [[ -z "$VERSION_OK" ]] ; then
PYTHON_VERSION=`"$PYTHON" -c "import sys; print('%s.%s' % sys.version_info[0:2]);"`
DISP_MIN_VERSION=`"$PYTHON" -c "print('%s.%s' % $MIN_VERSION)"`
echo "ERROR: Python $PYTHON_VERSION detected, but $DISP_MIN_VERSION+ is required."
echo "Please upgrade your Python distribution to install Databases."
exit 1
fi
REQUIREMENTS="requirements.txt"
VENV="venv"
PIP="$VENV/bin/pip"
set -x
"$PYTHON" -m venv "$VENV"
"$PIP" install -r "$REQUIREMENTS"
"$PIP" install -e .

View File

@ -1,16 +0,0 @@
#!/bin/sh -e
export PREFIX=""
if [ -d 'venv' ] ; then
export PREFIX="venv/bin/"
fi
set -x
${PREFIX}autoflake --in-place --recursive httpx tests setup.py
${PREFIX}isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --apply httpx tests setup.py
${PREFIX}black httpx tests setup.py
${PREFIX}flake8 --max-line-length=88 --ignore=W503,E203,B305 httpx tests setup.py
${PREFIX}mypy httpx --ignore-missing-imports --disallow-untyped-defs
scripts/clean

View File

@ -1,12 +0,0 @@
#!/bin/sh -e
export PACKAGE="httpx"
export PREFIX=""
if [ -d 'venv' ] ; then
export PREFIX="venv/bin/"
fi
set -x
PYTHONPATH=. ${PREFIX}pytest --ignore venv --cov tests --cov ${PACKAGE} --cov-report= ${@}
${PREFIX}coverage report --show-missing --fail-under=100

View File

@ -9,13 +9,6 @@ rfc3986==1.*
# Optional
brotlipy==0.7.*
# Documentation
mkdocs
mkdocs-material
# Testing
autoflake
black
cryptography
flake8
flake8-bugbear

View File

@ -1,5 +1,4 @@
import pytest
import rfc3986
from httpx import URL
from httpx.exceptions import InvalidURL

View File

@ -37,7 +37,7 @@ def test_bad_utf_like_encoding():
),
)
def test_guess_by_bom(encoding, expected):
data = u"\ufeff{}".encode(encoding)
data = "\ufeff{}".encode(encoding)
assert guess_json_utf(data) == expected