feat: add python 3.11 support (#392)

* feat: add python 3.11 support

- adds testing for python 3.11
- moves from black and flake8 to ruff for linting
- Updates coverage output from tests

* fix: fix dev dependency group

* fix: add installing dev dependencies

* ci: add toml

* ci: simplify coverage

* ci: update coverage
This commit is contained in:
Andrew
2023-02-20 23:12:01 -06:00
committed by GitHub
parent 6ae5ecefea
commit a752c42a28
15 changed files with 198 additions and 531 deletions

View File

@@ -4,3 +4,4 @@ nox-poetry==1.0.2
poetry==1.3.2
virtualenv==20.19.0
poetry-dynamic-versioning==0.21.3
toml==0.10.2

View File

@@ -17,13 +17,14 @@ jobs:
# - { python: "3.9", os: "ubuntu-latest", session: "mypy" }
# - { python: "3.8", os: "ubuntu-latest", session: "mypy" }
# - { python: "3.7", os: "ubuntu-latest", session: "mypy" }
- { python: "3.11", os: "ubuntu-latest", session: "tests" }
- { python: "3.10", os: "ubuntu-latest", session: "tests" }
- { python: "3.9", os: "ubuntu-latest", session: "tests" }
- { python: "3.8", os: "ubuntu-latest", session: "tests" }
- { python: "3.7", os: "ubuntu-latest", session: "tests" }
# poetry fails to install on windows
# - { python: "3.10", os: "windows-latest", session: "tests" }
# - { python: "3.10", os: "macos-latest", session: "tests" }
# - { python: "3.10", os: "ubuntu-latest", session: "typeguard" }
- { python: "3.11", os: "macos-latest", session: "tests" }
- { python: "3.10", os: "ubuntu-latest", session: "xdoctest" }
- { python: "3.10", os: "ubuntu-latest", session: "docs-build" }
@@ -63,7 +64,7 @@ jobs:
- name: Install Nox
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox
pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry
pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry toml
nox --version
- name: Compute pre-commit cache key
@@ -73,13 +74,15 @@ jobs:
run: |
import hashlib
import sys
import os
python = "py{}.{}".format(*sys.version_info[:2])
payload = sys.version.encode() + sys.executable.encode()
digest = hashlib.sha256(payload).hexdigest()
result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8])
print("::set-output name=result::{}".format(result))
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh:
fh.write(f"result={result}\n")
- name: Restore pre-commit cache
uses: actions/cache@v3.2.5
@@ -112,30 +115,6 @@ jobs:
runs-on: ubuntu-latest
needs: tests
steps:
- name: Check out the repository
uses: actions/checkout@v3.1.0
- name: Set up Python
uses: actions/setup-python@v4.5.0
with:
python-version: "3.10"
- name: Upgrade pip
run: |
pip install --constraint=.github/workflows/constraints.txt pip
pip --version
- name: Install Poetry
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt poetry
poetry --version
- name: Install Nox
run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox
pipx inject --pip-args=--constraint=.github/workflows/constraints.txt nox nox-poetry
nox --version
- name: Download coverage data
uses: actions/download-artifact@v3.0.2
with:
@@ -143,3 +122,6 @@ jobs:
- name: Upload coverage report
uses: codecov/codecov-action@v3.1.1
with:
files: .coverage.xml
verbose: true

View File

@@ -1,12 +1,20 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: "v0.0.249"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
types: [python]
- repo: local
hooks:
- id: black
name: black
entry: black
- id: bandit
name: bandit
entry: bandit
language: system
types: [python]
require_serial: true
args: ["-c", "bandit.yml"]
- id: check-added-large-files
name: Check for added large files
entry: check-added-large-files
@@ -27,14 +35,6 @@ repos:
language: system
types: [text]
stages: [commit, push, manual]
- id: flake8
name: flake8
entry: flake8
language: system
types: [python]
exclude: "^(test/*|examples/*|noxfile.py)"
require_serial: true
args: ["--config=.flake8"]
- id: pyupgrade
name: pyupgrade
description: Automatically upgrade syntax for newer versions.

3
bandit.yml Normal file
View File

@@ -0,0 +1,3 @@
exclude_dirs: ["test", "examples"]
assert_used:
skips: ["*/test_*.py", "noxfile.py"]

View File

@@ -7,6 +7,7 @@ from pathlib import Path
from textwrap import dedent
import nox
import toml
try:
from nox_poetry import Session
@@ -22,7 +23,7 @@ except ImportError:
package = "healthchecks_io"
python_versions = ["3.10", "3.9", "3.8", "3.7"]
python_versions = ["3.10", "3.11", "3.9", "3.8", "3.7"]
nox.needs_version = ">= 2021.6.6"
nox.options.sessions = (
"pre-commit",
@@ -43,6 +44,9 @@ test_requirements = (
"pytest-asyncio",
"pytest-lazy-fixture",
)
mypy_type_packages = ()
pyproject = toml.load("pyproject.toml")
test_requirements = pyproject["tool"]["poetry"]["group"]["dev"]["dependencies"].keys()
def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
@@ -61,8 +65,7 @@ def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
# quoting rules for Python and bash, but strip the outermost quotes so we
# can detect paths within the bindir, like <bindir>/python.
bindirs = [
bindir[1:-1] if bindir[0] in "'\"" else bindir
for bindir in (repr(session.bin), shlex.quote(session.bin))
bindir[1:-1] if bindir[0] in "'\"" else bindir for bindir in (repr(session.bin), shlex.quote(session.bin))
]
virtualenv = session.env.get("VIRTUAL_ENV")
@@ -99,10 +102,7 @@ def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
text = hook.read_text()
if not any(
Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text
for bindir in bindirs
):
if not any(Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text for bindir in bindirs):
continue
lines = text.splitlines()
@@ -144,9 +144,7 @@ def safety(session: Session) -> None:
session.install("safety")
# ignore https://github.com/pytest-dev/py/issues/287
# its an irresposnbily filed CVE causing nose
session.run(
"safety", "check", "--full-report", f"--file={requirements}", "--ignore=51457"
)
session.run("safety", "check", "--full-report", f"--file={requirements}", "--ignore=51457")
@session(python=python_versions)
@@ -171,32 +169,7 @@ def tests(session: Session) -> None:
"""Run the test suite."""
session.install(".")
session.install(*test_requirements)
try:
session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
finally:
if session.interactive:
session.notify("coverage", posargs=[])
@session(python=python_versions[0])
def coverage(session: Session) -> None:
"""Produce the coverage report."""
args = session.posargs or ["report"]
session.install("coverage[toml]")
if not session.posargs and any(Path().glob(".coverage.*")):
session.run("coverage", "combine")
session.run("coverage", *args)
@session(python=python_versions)
def typeguard(session: Session) -> None:
"""Runtime type checking using Typeguard."""
session.install(".")
session.install("pytest", "typeguard", "pygments")
session.run("pytest", f"--typeguard-packages={package}", *session.posargs)
session.run("poetry", "run", "pytest", *session.posargs)
@session(python=python_versions)

419
poetry.lock generated
View File

@@ -110,43 +110,6 @@ soupsieve = ">1.2"
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "black"
version = "22.12.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
{file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
{file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
{file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
{file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
{file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
{file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
{file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
{file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
{file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
{file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
{file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
]
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "certifi"
version = "2022.12.7"
@@ -391,18 +354,6 @@ files = [
[package.dependencies]
python-dateutil = "*"
[[package]]
name = "darglint"
version = "1.8.1"
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
files = [
{file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"},
{file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"},
]
[[package]]
name = "distlib"
version = "0.3.6"
@@ -462,6 +413,21 @@ files = [
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "execnet"
version = "1.9.0"
description = "execnet: rapid multi-Python deployment"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
]
[package.extras]
testing = ["pre-commit"]
[[package]]
name = "filelock"
version = "3.9.0"
@@ -478,112 +444,6 @@ files = [
docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"]
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
[package.dependencies]
importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""}
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "flake8-bandit"
version = "3.0.0"
description = "Automated security testing with bandit and flake8."
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "flake8_bandit-3.0.0-py2.py3-none-any.whl", hash = "sha256:61b617f4f7cdaa0e2b1e6bf7b68afb2b619a227bb3e3ae00dd36c213bd17900a"},
{file = "flake8_bandit-3.0.0.tar.gz", hash = "sha256:54d19427e6a8d50322a7b02e1841c0a7c22d856975f3459803320e0e18e2d6a1"},
]
[package.dependencies]
bandit = ">=1.7.3"
flake8 = "*"
flake8-polyfill = "*"
pycodestyle = "*"
[[package]]
name = "flake8-bugbear"
version = "23.2.13"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "flake8-bugbear-23.2.13.tar.gz", hash = "sha256:39259814a83f33c8409417ee12dd4050c9c0bb4c8707c12fc18ae62b2f3ddee1"},
{file = "flake8_bugbear-23.2.13-py3-none-any.whl", hash = "sha256:f136bd0ca2684f101168bba2310dec541e11aa6b252260c17dcf58d18069a740"},
]
[package.dependencies]
attrs = ">=19.2.0"
flake8 = ">=3.0.0"
[package.extras]
dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"]
[[package]]
name = "flake8-docstrings"
version = "1.7.0"
description = "Extension for flake8 which uses pydocstyle to check docstrings"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"},
{file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"},
]
[package.dependencies]
flake8 = ">=3"
pydocstyle = ">=2.1"
[[package]]
name = "flake8-polyfill"
version = "1.0.2"
description = "Polyfill package for Flake8 plugins"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"},
{file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"},
]
[package.dependencies]
flake8 = "*"
[[package]]
name = "flake8-rst-docstrings"
version = "0.3.0"
description = "Python docstring reStructuredText (RST) validator for flake8"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"},
{file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"},
]
[package.dependencies]
flake8 = ">=3"
pygments = "*"
restructuredtext-lint = "*"
[package.extras]
develop = ["build", "twine"]
[[package]]
name = "furo"
version = "2022.9.29"
@@ -619,14 +479,14 @@ smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
version = "3.1.30"
description = "GitPython is a python library used to interact with Git repositories"
version = "3.1.31"
description = "GitPython is a Python library used to interact with Git repositories"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"},
{file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"},
{file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"},
{file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"},
]
[package.dependencies]
@@ -735,14 +595,14 @@ files = [
[[package]]
name = "importlib-metadata"
version = "4.2.0"
version = "6.0.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"},
{file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"},
{file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
{file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
]
[package.dependencies]
@@ -750,8 +610,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "iniconfig"
@@ -791,7 +652,6 @@ category = "dev"
optional = false
python-versions = "*"
files = [
{file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"},
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
]
@@ -859,18 +719,6 @@ files = [
{file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"},
]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
[[package]]
name = "mypy"
version = "1.0.1"
@@ -961,18 +809,6 @@ files = [
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pathspec"
version = "0.11.0"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"},
{file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
]
[[package]]
name = "pbr"
version = "5.11.1"
@@ -985,39 +821,24 @@ files = [
{file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"},
]
[[package]]
name = "pep8-naming"
version = "0.13.2"
description = "Check PEP-8 naming conventions, plugin for flake8"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pep8-naming-0.13.2.tar.gz", hash = "sha256:93eef62f525fd12a6f8c98f4dcc17fa70baae2f37fa1f73bec00e3e44392fa48"},
{file = "pep8_naming-0.13.2-py3-none-any.whl", hash = "sha256:59e29e55c478db69cffbe14ab24b5bd2cd615c0413edf790d47d3fb7ba9a4e23"},
]
[package.dependencies]
flake8 = ">=3.9.1"
[[package]]
name = "platformdirs"
version = "2.6.2"
version = "3.0.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
{file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
{file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"},
{file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"},
]
[package.dependencies]
typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""}
[package.extras]
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
@@ -1074,18 +895,6 @@ files = [
"ruamel.yaml" = ">=0.15"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
[[package]]
name = "pydantic"
version = "1.10.5"
@@ -1139,37 +948,6 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pydocstyle"
version = "6.3.0"
description = "Python docstring style checker"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"},
{file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"},
]
[package.dependencies]
importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""}
snowballstemmer = ">=2.2.0"
[package.extras]
toml = ["tomli (>=1.2.3)"]
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
[[package]]
name = "pygments"
version = "2.14.0"
@@ -1225,18 +1003,6 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "pytest-async"
version = "0.1.1"
description = "pytest-async - Run your coroutine in event loop without decorator"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "pytest_async-0.1.1-py3-none-any.whl", hash = "sha256:11cc41eef82592951d56c2bb9b0e6ab21b2f0f00663e78d95694a80d965be930"},
{file = "pytest_async-0.1.1.tar.gz", hash = "sha256:0d6ffd3ebac2f3aa47d606dbae1984750268a89dc8caf4a908ba61c60299cdfd"},
]
[[package]]
name = "pytest-asyncio"
version = "0.20.3"
@@ -1309,6 +1075,27 @@ pytest = ">=5.0"
[package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "pytest-xdist"
version = "3.2.0"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-xdist-3.2.0.tar.gz", hash = "sha256:fa10f95a2564cd91652f2d132725183c3b590d9fdcdec09d3677386ecf4c1ce9"},
{file = "pytest_xdist-3.2.0-py3-none-any.whl", hash = "sha256:336098e3bbd8193276867cc87db8b22903c3927665dff9d1ac8684c02f597b68"},
]
[package.dependencies]
execnet = ">=1.1"
pytest = ">=6.2.0"
[package.extras]
psutil = ["psutil (>=3.0)"]
setproctitle = ["setproctitle"]
testing = ["filelock"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
@@ -1453,20 +1240,6 @@ files = [
[package.dependencies]
httpx = ">=0.21.0"
[[package]]
name = "restructuredtext-lint"
version = "1.4.0"
description = "reStructuredText linter"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"},
]
[package.dependencies]
docutils = ">=0.11,<1.0"
[[package]]
name = "rfc3986"
version = "1.5.0"
@@ -1520,7 +1293,6 @@ files = [
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"},
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"},
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"},
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"},
@@ -1548,6 +1320,33 @@ files = [
{file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"},
]
[[package]]
name = "ruff"
version = "0.0.249"
description = "An extremely fast Python linter, written in Rust."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.0.249-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:03a26f1cb5605508de49d921d0970895b9e3ad4021f776a53be18fa95a4fc25b"},
{file = "ruff-0.0.249-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:46537d960221e97adc6a3556159ab3ae4b722b9985de13c50b436732d4659af0"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2dcc1f3053092aeedef8e47704e301b74687fa480fe5e7ebef2b0eb2e4a0bd"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:855cfe47d146a1eb68347025c7b5ad651c083343de6cb7ccf90585bda3e381db"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf3af16748c8539a48451edbcb687994eccc6a764c95f42de22195007ae13a24"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2815e05ba168dee6708dbbdab8d0c145bb3b0085c91ee552839c1c18a52f6cb1"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ab43389216cc8403db84992977e6f5e8fee83bd10aca05e1f2f262754cd8384"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b67c44ab260d3a838ec237c7234be1098bf2ef1421036fbbb229698513d1fc3"},
{file = "ruff-0.0.249-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d859744e1cc95ad5e52c4642509b3abb5ea0833f0529c380c2731b8cab5726"},
{file = "ruff-0.0.249-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6f494276ee281eb09c7026cc17df1bfc2fe59ab39a87196014ce093ff27f1a0"},
{file = "ruff-0.0.249-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:be23c57b9551d8fcf559755e5bc56ac5bcbc3215fc8a3190ea6ed1bb9133d8dd"},
{file = "ruff-0.0.249-py3-none-musllinux_1_2_i686.whl", hash = "sha256:980a3bce8ba38c9b47bc000915e80a672add9f7e9c5b128375486ec8cd8f860d"},
{file = "ruff-0.0.249-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f1e988e9365b11c6d7796c0d4a0556f6a26f0627fe57e9e7411ff91f421fb502"},
{file = "ruff-0.0.249-py3-none-win32.whl", hash = "sha256:f4837a7e6d1ff81cb027695deb28793e0945cca8d88e87b46ff845ef38d52c82"},
{file = "ruff-0.0.249-py3-none-win_amd64.whl", hash = "sha256:4cc437ab55a35088008dbe9db598cd8e240b5f70fb88eb8ab6fa1de529007f30"},
{file = "ruff-0.0.249-py3-none-win_arm64.whl", hash = "sha256:3d2d11a7b750433f3acec30641faab673d101aa86a2ddfe4af8bcfa773b178e2"},
{file = "ruff-0.0.249.tar.gz", hash = "sha256:b590689f08ecef971c45555cbda6854cdf48f3828fc326802828e851b1a14b3d"},
]
[[package]]
name = "safety"
version = "2.3.5"
@@ -1574,14 +1373,14 @@ gitlab = ["python-gitlab (>=1.3.0)"]
[[package]]
name = "setuptools"
version = "67.3.2"
version = "67.3.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-67.3.2-py3-none-any.whl", hash = "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48"},
{file = "setuptools-67.3.2.tar.gz", hash = "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012"},
{file = "setuptools-67.3.3-py3-none-any.whl", hash = "sha256:9d3de8591bd6f6522594406fa46a6418eabd0562dacb267f8556675762801514"},
{file = "setuptools-67.3.3.tar.gz", hash = "sha256:ed4e75fafe103c79b692f217158ba87edf38d31004b9dbc1913debb48793c828"},
]
[package.extras]
@@ -1651,14 +1450,14 @@ files = [
[[package]]
name = "sphinx"
version = "4.3.2"
version = "4.5.0"
description = "Python documentation generator"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"},
{file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"},
{file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"},
{file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"},
]
[package.dependencies]
@@ -1667,11 +1466,11 @@ babel = ">=1.3"
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.14,<0.18"
imagesize = "*"
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
Jinja2 = ">=2.3"
packaging = "*"
Pygments = ">=2.0"
requests = ">=2.5.0"
setuptools = "*"
snowballstemmer = ">=1.1"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
@@ -1682,7 +1481,7 @@ sphinxcontrib-serializinghtml = ">=1.1.5"
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"]
test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"]
[[package]]
@@ -1925,22 +1724,6 @@ files = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
]
[[package]]
name = "typeguard"
version = "2.13.3"
description = "Run-time type checker for Python"
category = "dev"
optional = false
python-versions = ">=3.5.3"
files = [
{file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"},
{file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"},
]
[package.extras]
doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["mypy", "pytest", "typing-extensions"]
[[package]]
name = "types-croniter"
version = "1.3.2.5"
@@ -1996,25 +1779,25 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "virtualenv"
version = "20.16.2"
version = "20.19.0"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"},
{file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"},
{file = "virtualenv-20.19.0-py3-none-any.whl", hash = "sha256:54eb59e7352b573aa04d53f80fc9736ed0ad5143af445a1e539aada6eb947dd1"},
{file = "virtualenv-20.19.0.tar.gz", hash = "sha256:37a640ba82ed40b226599c522d411e4be5edb339a0c0de030c0dc7b646d61590"},
]
[package.dependencies]
distlib = ">=0.3.1,<1"
filelock = ">=3.2,<4"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
platformdirs = ">=2,<3"
distlib = ">=0.3.6,<1"
filelock = ">=3.4.1,<4"
importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""}
platformdirs = ">=2.4,<4"
[package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"]
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"]
test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"]
[[package]]
name = "xdoctest"
@@ -2048,14 +1831,14 @@ tests-strict = ["codecov (==2.0.15)", "pytest (==4.6.0)", "pytest (==4.6.0)", "p
[[package]]
name = "zipp"
version = "3.13.0"
version = "3.14.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"},
{file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"},
{file = "zipp-3.14.0-py3-none-any.whl", hash = "sha256:188834565033387710d046e3fe96acfc9b5e86cbca7f39ff69cf21a4128198b7"},
{file = "zipp-3.14.0.tar.gz", hash = "sha256:9e5421e176ef5ab4c0ad896624e87a7b2f07aca746c9b2aa305952800cb8eecb"},
]
[package.extras]
@@ -2065,4 +1848,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "266b1df286e6f23b7ae248d165a5af79ae10c2b7458c233b0308377b9bdcdff9"
content-hash = "f53f32b117c664e5f79577caf74d69872362ced2f029a23e29832097be26e62d"

View File

@@ -31,37 +31,29 @@ httpx = ">=0.23.0,<0.24.0"
croniter = "^1.1.0"
pytz = ">=2021.3,<2023.0"
[tool.poetry.dev-dependencies]
[tool.poetry.group.dev.dependencies]
pytest = "^7.2.1"
coverage = {extras = ["toml"], version = "^7.1"}
safety = "^2.3.5"
mypy = "^1.0"
typeguard = "^2.13.2"
xdoctest = {extras = ["colors"], version = "^1.1.1"}
sphinx = "^4.3.2"
sphinx-autobuild = ">=2021.3.14"
pre-commit = "^2.21.0"
flake8 = "^4.0.1"
black = ">=21.10b0"
flake8-bandit = "^3.0.0"
flake8-bugbear = ">=21.9.2"
flake8-docstrings = "^1.7.0"
flake8-rst-docstrings = "^0.3.0"
pep8-naming = "^0.13.2"
darglint = "^1.8.1"
reorder-python-imports = "^3.9.0"
pre-commit-hooks = "^4.4.0"
Pygments = "^2.14.0"
pyupgrade = "^3.3.1"
furo = ">=2021.11.12"
pytest-cov = "^4.0.0"
types-croniter = "^1.3.2"
types-pytz = "^2022.7.1"
pytest_async = "^0.1.1"
pytest-asyncio = "^0.20.3"
respx = "^0.20.1"
pytest-mock = "^3.10.0"
pytest-lazy-fixture = "^0.6.3"
pytest-xdist = "^3.2.0"
ruff = "^0.0.249"
bandit = "^1.7.4"
[tool.coverage.paths]
source = ["src", "*/site-packages"]
@@ -87,10 +79,9 @@ show_error_context = true
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry.core.masonry.api"
[tool.poetry-dynamic-versioning]
enable = true
vcs = "git"
dirty = true
[tool.pytest.ini_options]
addopts = "-n 4 --ignore examples --cov=healthchecks_io --cov-report xml:.coverage.xml --cov-report=term-missing --cov-fail-under 91"
[tool.poetry-dynamic-versioning.substitution]
files = ["src/healthchecks_io/__init__.py"]
[tool.ruff]
line-length = 120
target-version = "py37"

View File

@@ -83,7 +83,8 @@ class AbstractClient(ABC):
endpoint (str): Endpoint to request
Raises:
BadAPIRequestError: Raised if you pass a uuid and a slug, or if pinging by a slug and do not have a ping key set
BadAPIRequestError: Raised if you pass a uuid and a slug, or if pinging by a slug and do not have a
ping key set
Returns:
str: url for this

View File

@@ -42,9 +42,7 @@ class AsyncClient(AbstractClient):
client (Optional[HTTPXAsyncClient], optional): A httpx.Asyncclient. If not
passed in, one will be created for this object. Defaults to None.
"""
self._client: HTTPXAsyncClient = (
HTTPXAsyncClient() if client is None else client
)
self._client: HTTPXAsyncClient = HTTPXAsyncClient() if client is None else client
super().__init__(
api_key=api_key,
ping_key=ping_key,
@@ -53,9 +51,7 @@ class AsyncClient(AbstractClient):
api_version=api_version,
)
self._client.headers["X-Api-Key"] = self._api_key
self._client.headers[
"user-agent"
] = f"py-healthchecks.io-async/{client_version}"
self._client.headers["user-agent"] = f"py-healthchecks.io-async/{client_version}"
self._client.headers["Content-type"] = "application/json"
async def __aenter__(self) -> "AsyncClient":
@@ -97,9 +93,7 @@ class AsyncClient(AbstractClient):
Check: check that was just created
"""
request_url = self._get_api_request_url("checks/")
response = self.check_response(
await self._client.post(request_url, json=new_check.dict(exclude_none=True))
)
response = self.check_response(await self._client.post(request_url, json=new_check.dict(exclude_none=True)))
return Check.from_api_result(response.json())
async def update_check(self, uuid: str, update_check: CheckCreate) -> Check:
@@ -143,16 +137,11 @@ class AsyncClient(AbstractClient):
request_url = self._get_api_request_url("checks/")
if tags is not None:
for tag in tags:
request_url = self._add_url_params(
request_url, {"tag": tag}, replace=False
)
request_url = self._add_url_params(request_url, {"tag": tag}, replace=False)
response = self.check_response(await self._client.get(request_url))
return [
Check.from_api_result(check_data)
for check_data in response.json()["checks"]
]
return [Check.from_api_result(check_data) for check_data in response.json()["checks"]]
async def get_check(self, check_id: str) -> Check:
"""Get a single check by id.
@@ -247,10 +236,7 @@ class AsyncClient(AbstractClient):
"""
request_url = self._get_api_request_url(f"checks/{check_id}/pings/")
response = self.check_response(await self._client.get(request_url))
return [
CheckPings.from_api_result(check_data)
for check_data in response.json()["pings"]
]
return [CheckPings.from_api_result(check_data) for check_data in response.json()["pings"]]
async def get_check_flips(
self,
@@ -274,8 +260,10 @@ class AsyncClient(AbstractClient):
Args:
check_id (str): check uuid
seconds (Optional[int], optional): Returns the flips from the last value seconds. Defaults to None.
start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.. Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.. Defaults to None.
start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.
Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.
Defaults to None.
Returns:
List[CheckStatuses]: List of status flips for this check
@@ -307,10 +295,7 @@ class AsyncClient(AbstractClient):
"""
request_url = self._get_api_request_url("channels/")
response = self.check_response(await self._client.get(request_url))
return [
Integration.from_api_result(integration_dict)
for integration_dict in response.json()["channels"]
]
return [Integration.from_api_result(integration_dict) for integration_dict in response.json()["channels"]]
async def get_badges(self) -> Dict[str, Badges]:
"""Returns a dict of all tags in the project, with badge URLs for each tag.
@@ -321,7 +306,8 @@ class AsyncClient(AbstractClient):
shields: returns JSON in a Shields.io compatible format.
In addition, badges have 2-state and 3-state variations:
svg, json, shields: reports two states: "up" and "down". It considers any checks in the grace period as still "up".
svg, json, shields: reports two states: "up" and "down". It considers any checks in the grace period
as still "up".
svg3, json3, shields3: reports three states: "up", "late", and "down".
The response includes a special * entry: this pseudo-tag reports the overal status
@@ -337,14 +323,9 @@ class AsyncClient(AbstractClient):
"""
request_url = self._get_api_request_url("badges/")
response = self.check_response(await self._client.get(request_url))
return {
key: Badges.from_api_result(item)
for key, item in response.json()["badges"].items()
}
return {key: Badges.from_api_result(item) for key, item in response.json()["badges"].items()}
async def success_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
async def success_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that a job has completed successfully.
Can also be used to indicate a continuously running process is still running and healthy.
@@ -375,14 +356,10 @@ class AsyncClient(AbstractClient):
Tuple[bool, str]: success (true or false) and the response text
"""
ping_url = self._get_ping_url(uuid, slug, "")
response = self.check_ping_response(
await self._client.post(ping_url, content=data)
)
response = self.check_ping_response(await self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
async def start_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
async def start_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Sends a "job has started!" message to Healthchecks.io.
Sending a "start" signal is optional, but it enables a few extra features:
@@ -415,14 +392,10 @@ class AsyncClient(AbstractClient):
Tuple[bool, str]: success (true or false) and the response text
"""
ping_url = self._get_ping_url(uuid, slug, "/start")
response = self.check_ping_response(
await self._client.post(ping_url, content=data)
)
response = self.check_ping_response(await self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
async def fail_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
async def fail_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed.
Actively signaling a failure minimizes the delay from your monitored service failing to you receiving an alert.
@@ -453,14 +426,10 @@ class AsyncClient(AbstractClient):
Tuple[bool, str]: success (true or false) and the response text
"""
ping_url = self._get_ping_url(uuid, slug, "/fail")
response = self.check_ping_response(
await self._client.post(ping_url, content=data)
)
response = self.check_ping_response(await self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
async def exit_code_ping(
self, exit_code: int, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
async def exit_code_ping(self, exit_code: int, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed.
Actively signaling a failure minimizes the delay from your monitored service failing to you receiving an alert.
@@ -492,7 +461,5 @@ class AsyncClient(AbstractClient):
Tuple[bool, str]: success (true or false) and the response text
"""
ping_url = self._get_ping_url(uuid, slug, f"/{exit_code}")
response = self.check_ping_response(
await self._client.post(ping_url, content=data)
)
response = self.check_ping_response(await self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)

View File

@@ -90,16 +90,11 @@ class Client(AbstractClient):
request_url = self._get_api_request_url("checks/")
if tags is not None:
for tag in tags:
request_url = self._add_url_params(
request_url, {"tag": tag}, replace=False
)
request_url = self._add_url_params(request_url, {"tag": tag}, replace=False)
response = self.check_response(self._client.get(request_url))
return [
checks.Check.from_api_result(check_data)
for check_data in response.json()["checks"]
]
return [checks.Check.from_api_result(check_data) for check_data in response.json()["checks"]]
def get_check(self, check_id: str) -> checks.Check:
"""Get a single check by id.
@@ -137,9 +132,7 @@ class Client(AbstractClient):
Check: check that was just created
"""
request_url = self._get_api_request_url("checks/")
response = self.check_response(
self._client.post(request_url, json=new_check.dict(exclude_none=True))
)
response = self.check_response(self._client.post(request_url, json=new_check.dict(exclude_none=True)))
return Check.from_api_result(response.json())
def update_check(self, uuid: str, update_check: CheckCreate) -> Check:
@@ -236,10 +229,7 @@ class Client(AbstractClient):
"""
request_url = self._get_api_request_url(f"checks/{check_id}/pings/")
response = self.check_response(self._client.get(request_url))
return [
checks.CheckPings.from_api_result(check_data)
for check_data in response.json()["pings"]
]
return [checks.CheckPings.from_api_result(check_data) for check_data in response.json()["pings"]]
def get_check_flips(
self,
@@ -261,8 +251,10 @@ class Client(AbstractClient):
Args:
check_id (str): check uuid
seconds (Optional[int], optional): Returns the flips from the last value seconds. Defaults to None.
start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.. Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.. Defaults to None.
start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.
Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.
Defaults to None.
Returns:
List[checks.CheckStatuses]: List of status flips for this check
@@ -307,7 +299,8 @@ class Client(AbstractClient):
shields: returns JSON in a Shields.io compatible format.
In addition, badges have 2-state and 3-state variations:
svg, json, shields: reports two states: "up" and "down". It considers any checks in the grace period as still "up".
svg, json, shields: reports two states: "up" and "down". It considers any checks in the grace period
as still "up".
svg3, json3, shields3: reports three states: "up", "late", and "down".
The response includes a special * entry: this pseudo-tag reports the overal status
@@ -322,14 +315,9 @@ class Client(AbstractClient):
"""
request_url = self._get_api_request_url("badges/")
response = self.check_response(self._client.get(request_url))
return {
key: badges.Badges.from_api_result(item)
for key, item in response.json()["badges"].items()
}
return {key: badges.Badges.from_api_result(item) for key, item in response.json()["badges"].items()}
def success_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
def success_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that a job has completed successfully.
Can also be used to indicate a continuously running process is still running and healthy.
@@ -363,9 +351,7 @@ class Client(AbstractClient):
response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
def start_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
def start_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Sends a "job has started!" message to Healthchecks.io.
Sending a "start" signal is optional, but it enables a few extra features:
@@ -401,9 +387,7 @@ class Client(AbstractClient):
response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
def fail_ping(
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
def fail_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed.
Actively signaling a failure minimizes the delay from your monitored service failing to you receiving an alert.
@@ -437,9 +421,7 @@ class Client(AbstractClient):
response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text)
def exit_code_ping(
self, exit_code: int, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
def exit_code_ping(self, exit_code: int, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed.
Actively signaling a failure minimizes the delay from your monitored service failing to you receiving an alert.

View File

@@ -42,15 +42,14 @@ class Check(BaseModel):
uuid: Optional[str]
@validator("uuid", always=True)
def validate_uuid(
cls, value: Optional[str], values: Dict[str, Any] # noqa: B902
) -> Optional[str]:
def validate_uuid(cls, value: Optional[str], values: Dict[str, Any]) -> Optional[str]: # noqa: B902
"""Tries to set the uuid from the ping_url.
Will return none if a read only token is used because it cannot retrieve the UUID of a check
"""
if value is None and values.get("ping_url", None) is not None:
# url is like healthchecks.io/ping/8f57b84b-86c2-4546-8923-03f83d27604a, so we want just the UUID off the end
# url is like healthchecks.io/ping/8f57b84b-86c2-4546-8923-03f83d27604a, so we want just the
# UUID off the end
# Parse the url, grab the path and then just get the name using pathlib
path = PurePath(str(urlparse(values.get("ping_url")).path))
return path.name
@@ -66,9 +65,7 @@ class CheckCreate(BaseModel):
"""Pydantic object for creating a check."""
name: Optional[str] = Field("", description="Name of the check")
tags: Optional[str] = Field(
"", description="String separated list of tags to apply"
)
tags: Optional[str] = Field("", description="String separated list of tags to apply")
desc: Optional[str] = Field("", description="Description of the check")
timeout: Optional[int] = Field(
86400,
@@ -147,9 +144,7 @@ class CheckCreate(BaseModel):
def validate_methods(cls, value: str) -> str:
"""Validate that methods."""
if value not in ("", "POST"):
raise ValueError(
"Methods is invalid, it should be either an empty string or POST"
)
raise ValueError("Methods is invalid, it should be either an empty string or POST")
return value
@validator("unique")
@@ -167,9 +162,7 @@ class CheckUpdate(CheckCreate):
"""Pydantic object for updating a check."""
name: Optional[str] = Field(None, description="Name of the check")
tags: Optional[str] = Field(
None, description="String separated list of tags to apply"
)
tags: Optional[str] = Field(None, description="String separated list of tags to apply")
timeout: Optional[int] = Field(
None,
description="The expected period of this check in seconds.",
@@ -243,9 +236,7 @@ class CheckPings(BaseModel):
duration: Optional[float] = None
@classmethod
def from_api_result(
cls, ping_dict: Dict[str, Union[str, int, datetime]]
) -> "CheckPings":
def from_api_result(cls, ping_dict: Dict[str, Union[str, int, datetime]]) -> "CheckPings":
"""Converts a dictionary from the healthchecks api into a CheckPings object."""
ping_dict["number_of_pings"] = ping_dict["n"]
ping_dict["user_agent"] = ping_dict["ua"]

View File

@@ -10,7 +10,6 @@ from healthchecks_io import HCAPIAuthError
from healthchecks_io import HCAPIError
from healthchecks_io import HCAPIRateLimitError
from healthchecks_io import NonUniqueSlugError
from healthchecks_io.client._abstract import AbstractClient
def test_abstract_add_url_params(test_abstract_client):

View File

@@ -1,7 +1,6 @@
from urllib.parse import urljoin
import pytest
import respx
from httpx import AsyncClient as HTTPXAsyncClient
from httpx import Response

View File

@@ -1,13 +1,9 @@
from urllib.parse import urljoin
import pytest
import respx
from httpx import Client as HTTPXClient
from httpx import Response
from healthchecks_io import CheckCreate
from healthchecks_io import CheckTrap
from healthchecks_io import CheckUpdate
from healthchecks_io import PingFailedError
from healthchecks_io import WrongClientError
@@ -19,7 +15,7 @@ def test_check_trap_sync(respx_mock, test_client):
success_url = urljoin(test_client._ping_url, "test")
respx_mock.post(success_url).mock(return_value=Response(status_code=200, text="OK"))
with CheckTrap(test_client, uuid="test") as ct:
with CheckTrap(test_client, uuid="test"):
pass
@@ -28,7 +24,7 @@ def test_check_trap_sync_failed_ping(respx_mock, test_client):
start_url = urljoin(test_client._ping_url, "test/start")
respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK"))
with pytest.raises(PingFailedError):
with CheckTrap(test_client, uuid="test") as ct:
with CheckTrap(test_client, uuid="test"):
pass
@@ -39,7 +35,7 @@ def test_check_trap_sync_exception(respx_mock, test_client):
fail_url = urljoin(test_client._ping_url, "test/fail")
respx_mock.post(fail_url).mock(return_value=Response(status_code=200, text="OK"))
with pytest.raises(Exception):
with CheckTrap(test_client, uuid="test") as ct:
with CheckTrap(test_client, uuid="test"):
raise Exception("Exception")
@@ -51,7 +47,7 @@ async def test_check_trap_async(respx_mock, test_async_client):
success_url = urljoin(test_async_client._ping_url, "test")
respx_mock.post(success_url).mock(return_value=Response(status_code=200, text="OK"))
async with CheckTrap(test_async_client, uuid="test") as ct:
async with CheckTrap(test_async_client, uuid="test"):
pass
@@ -61,7 +57,7 @@ async def test_check_trap_async_failed_ping(respx_mock, test_async_client):
start_url = urljoin(test_async_client._ping_url, "test/start")
respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK"))
with pytest.raises(PingFailedError):
async with CheckTrap(test_async_client, uuid="test") as ct:
async with CheckTrap(test_async_client, uuid="test"):
pass
@@ -74,7 +70,7 @@ async def test_check_trap_async_exception(respx_mock, test_async_client):
respx_mock.post(fail_url).mock(return_value=Response(status_code=200, text="OK"))
with pytest.raises(Exception):
async with CheckTrap(test_async_client, uuid="test") as ct:
async with CheckTrap(test_async_client, uuid="test"):
raise Exception("Exception")
@@ -82,11 +78,11 @@ async def test_check_trap_async_exception(respx_mock, test_async_client):
async def test_check_trap_wrong_client_error(test_client, test_async_client):
with pytest.raises(WrongClientError):
async with CheckTrap(test_client, uuid="test") as ct:
async with CheckTrap(test_client, uuid="test"):
pass
with pytest.raises(WrongClientError):
with CheckTrap(test_async_client, uuid="test") as ct:
with CheckTrap(test_async_client, uuid="test"):
pass

View File

@@ -1,7 +1,6 @@
from urllib.parse import urljoin
import pytest
import respx
from httpx import Client as HTTPXClient
from httpx import Response