Use nox for automation and CI (#128)
This commit is contained in:
parent
95dad247b5
commit
80dc3e274e
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ __pycache__/
|
||||
htmlcov/
|
||||
*.egg-info/
|
||||
venv/
|
||||
.nox
|
||||
|
||||
3
.isort.cfg
Normal file
3
.isort.cfg
Normal 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
|
||||
33
.travis.yml
33
.travis.yml
@ -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
|
||||
|
||||
@ -86,6 +86,8 @@ __all__ = [
|
||||
"TimeoutConfig",
|
||||
"VerifyTypes",
|
||||
"HTTPConnection",
|
||||
"BasePoolSemaphore",
|
||||
"BaseBackgroundManager",
|
||||
"ConnectionPool",
|
||||
"ConnectTimeout",
|
||||
"CookieConflict",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
60
noxfile.py
Normal 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",
|
||||
)
|
||||
@ -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 .
|
||||
16
scripts/lint
16
scripts/lint
@ -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
|
||||
12
scripts/test
12
scripts/test
@ -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
|
||||
@ -9,13 +9,6 @@ rfc3986==1.*
|
||||
# Optional
|
||||
brotlipy==0.7.*
|
||||
|
||||
# Documentation
|
||||
mkdocs
|
||||
mkdocs-material
|
||||
|
||||
# Testing
|
||||
autoflake
|
||||
black
|
||||
cryptography
|
||||
flake8
|
||||
flake8-bugbear
|
||||
@ -1,5 +1,4 @@
|
||||
import pytest
|
||||
import rfc3986
|
||||
|
||||
from httpx import URL
|
||||
from httpx.exceptions import InvalidURL
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user