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 poetry==1.3.2
virtualenv==20.19.0 virtualenv==20.19.0
poetry-dynamic-versioning==0.21.3 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.9", os: "ubuntu-latest", session: "mypy" }
# - { python: "3.8", os: "ubuntu-latest", session: "mypy" } # - { python: "3.8", os: "ubuntu-latest", session: "mypy" }
# - { python: "3.7", 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.10", os: "ubuntu-latest", session: "tests" }
- { python: "3.9", os: "ubuntu-latest", session: "tests" } - { python: "3.9", os: "ubuntu-latest", session: "tests" }
- { python: "3.8", os: "ubuntu-latest", session: "tests" } - { python: "3.8", os: "ubuntu-latest", session: "tests" }
- { python: "3.7", 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: "windows-latest", session: "tests" }
# - { python: "3.10", os: "macos-latest", session: "tests" } - { python: "3.11", os: "macos-latest", session: "tests" }
# - { python: "3.10", os: "ubuntu-latest", session: "typeguard" }
- { python: "3.10", os: "ubuntu-latest", session: "xdoctest" } - { python: "3.10", os: "ubuntu-latest", session: "xdoctest" }
- { python: "3.10", os: "ubuntu-latest", session: "docs-build" } - { python: "3.10", os: "ubuntu-latest", session: "docs-build" }
@@ -63,7 +64,7 @@ jobs:
- name: Install Nox - name: Install Nox
run: | run: |
pipx install --pip-args=--constraint=.github/workflows/constraints.txt nox 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 nox --version
- name: Compute pre-commit cache key - name: Compute pre-commit cache key
@@ -73,13 +74,15 @@ jobs:
run: | run: |
import hashlib import hashlib
import sys import sys
import os
python = "py{}.{}".format(*sys.version_info[:2]) python = "py{}.{}".format(*sys.version_info[:2])
payload = sys.version.encode() + sys.executable.encode() payload = sys.version.encode() + sys.executable.encode()
digest = hashlib.sha256(payload).hexdigest() digest = hashlib.sha256(payload).hexdigest()
result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8]) 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 - name: Restore pre-commit cache
uses: actions/cache@v3.2.5 uses: actions/cache@v3.2.5
@@ -112,30 +115,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: tests needs: tests
steps: 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 - name: Download coverage data
uses: actions/download-artifact@v3.0.2 uses: actions/download-artifact@v3.0.2
with: with:
@@ -143,3 +122,6 @@ jobs:
- name: Upload coverage report - name: Upload coverage report
uses: codecov/codecov-action@v3.1.1 uses: codecov/codecov-action@v3.1.1
with:
files: .coverage.xml
verbose: true

View File

@@ -1,12 +1,20 @@
repos: 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 - repo: local
hooks: hooks:
- id: black - id: bandit
name: black name: bandit
entry: black entry: bandit
language: system language: system
types: [python] types: [python]
require_serial: true require_serial: true
args: ["-c", "bandit.yml"]
- id: check-added-large-files - id: check-added-large-files
name: Check for added large files name: Check for added large files
entry: check-added-large-files entry: check-added-large-files
@@ -27,14 +35,6 @@ repos:
language: system language: system
types: [text] types: [text]
stages: [commit, push, manual] 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 - id: pyupgrade
name: pyupgrade name: pyupgrade
description: Automatically upgrade syntax for newer versions. 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 from textwrap import dedent
import nox import nox
import toml
try: try:
from nox_poetry import Session from nox_poetry import Session
@@ -22,7 +23,7 @@ except ImportError:
package = "healthchecks_io" 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.needs_version = ">= 2021.6.6"
nox.options.sessions = ( nox.options.sessions = (
"pre-commit", "pre-commit",
@@ -43,6 +44,9 @@ test_requirements = (
"pytest-asyncio", "pytest-asyncio",
"pytest-lazy-fixture", "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: 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 # quoting rules for Python and bash, but strip the outermost quotes so we
# can detect paths within the bindir, like <bindir>/python. # can detect paths within the bindir, like <bindir>/python.
bindirs = [ bindirs = [
bindir[1:-1] if bindir[0] in "'\"" else bindir bindir[1:-1] if bindir[0] in "'\"" else bindir for bindir in (repr(session.bin), shlex.quote(session.bin))
for bindir in (repr(session.bin), shlex.quote(session.bin))
] ]
virtualenv = session.env.get("VIRTUAL_ENV") virtualenv = session.env.get("VIRTUAL_ENV")
@@ -99,10 +102,7 @@ def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
text = hook.read_text() text = hook.read_text()
if not any( if not any(Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text for bindir in bindirs):
Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text
for bindir in bindirs
):
continue continue
lines = text.splitlines() lines = text.splitlines()
@@ -144,9 +144,7 @@ def safety(session: Session) -> None:
session.install("safety") session.install("safety")
# ignore https://github.com/pytest-dev/py/issues/287 # ignore https://github.com/pytest-dev/py/issues/287
# its an irresposnbily filed CVE causing nose # its an irresposnbily filed CVE causing nose
session.run( session.run("safety", "check", "--full-report", f"--file={requirements}", "--ignore=51457")
"safety", "check", "--full-report", f"--file={requirements}", "--ignore=51457"
)
@session(python=python_versions) @session(python=python_versions)
@@ -171,32 +169,7 @@ def tests(session: Session) -> None:
"""Run the test suite.""" """Run the test suite."""
session.install(".") session.install(".")
session.install(*test_requirements) session.install(*test_requirements)
try: session.run("poetry", "run", "pytest", *session.posargs)
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(python=python_versions) @session(python=python_versions)

419
poetry.lock generated
View File

@@ -110,43 +110,6 @@ soupsieve = ">1.2"
html5lib = ["html5lib"] html5lib = ["html5lib"]
lxml = ["lxml"] 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]] [[package]]
name = "certifi" name = "certifi"
version = "2022.12.7" version = "2022.12.7"
@@ -391,18 +354,6 @@ files = [
[package.dependencies] [package.dependencies]
python-dateutil = "*" 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]] [[package]]
name = "distlib" name = "distlib"
version = "0.3.6" version = "0.3.6"
@@ -462,6 +413,21 @@ files = [
[package.extras] [package.extras]
test = ["pytest (>=6)"] 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]] [[package]]
name = "filelock" name = "filelock"
version = "3.9.0" version = "3.9.0"
@@ -478,112 +444,6 @@ files = [
docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] 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)"] 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]] [[package]]
name = "furo" name = "furo"
version = "2022.9.29" version = "2022.9.29"
@@ -619,14 +479,14 @@ smmap = ">=3.0.1,<6"
[[package]] [[package]]
name = "gitpython" name = "gitpython"
version = "3.1.30" version = "3.1.31"
description = "GitPython is a python library used to interact with Git repositories" description = "GitPython is a Python library used to interact with Git repositories"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"},
{file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"},
] ]
[package.dependencies] [package.dependencies]
@@ -735,14 +595,14 @@ files = [
[[package]] [[package]]
name = "importlib-metadata" name = "importlib-metadata"
version = "4.2.0" version = "6.0.0"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
files = [ files = [
{file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
{file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
] ]
[package.dependencies] [package.dependencies]
@@ -750,8 +610,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5" zipp = ">=0.5"
[package.extras] [package.extras]
docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
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"] 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]] [[package]]
name = "iniconfig" name = "iniconfig"
@@ -791,7 +652,6 @@ category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"},
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, {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"}, {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]] [[package]]
name = "mypy" name = "mypy"
version = "1.0.1" version = "1.0.1"
@@ -961,18 +809,6 @@ files = [
[package.dependencies] [package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 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]] [[package]]
name = "pbr" name = "pbr"
version = "5.11.1" version = "5.11.1"
@@ -985,39 +821,24 @@ files = [
{file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, {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]] [[package]]
name = "platformdirs" 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\"." description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, {file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"},
{file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, {file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"},
] ]
[package.dependencies] [package.dependencies]
typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""}
[package.extras] [package.extras]
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] 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)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
@@ -1074,18 +895,6 @@ files = [
"ruamel.yaml" = ">=0.15" "ruamel.yaml" = ">=0.15"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 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]] [[package]]
name = "pydantic" name = "pydantic"
version = "1.10.5" version = "1.10.5"
@@ -1139,37 +948,6 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"] dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"] 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]] [[package]]
name = "pygments" name = "pygments"
version = "2.14.0" version = "2.14.0"
@@ -1225,18 +1003,6 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras] [package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 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]] [[package]]
name = "pytest-asyncio" name = "pytest-asyncio"
version = "0.20.3" version = "0.20.3"
@@ -1309,6 +1075,27 @@ pytest = ">=5.0"
[package.extras] [package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"] 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]] [[package]]
name = "python-dateutil" name = "python-dateutil"
version = "2.8.2" version = "2.8.2"
@@ -1453,20 +1240,6 @@ files = [
[package.dependencies] [package.dependencies]
httpx = ">=0.21.0" 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]] [[package]]
name = "rfc3986" name = "rfc3986"
version = "1.5.0" 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-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_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-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_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-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"},
{file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, {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"}, {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]] [[package]]
name = "safety" name = "safety"
version = "2.3.5" version = "2.3.5"
@@ -1574,14 +1373,14 @@ gitlab = ["python-gitlab (>=1.3.0)"]
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "67.3.2" version = "67.3.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "setuptools-67.3.2-py3-none-any.whl", hash = "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48"}, {file = "setuptools-67.3.3-py3-none-any.whl", hash = "sha256:9d3de8591bd6f6522594406fa46a6418eabd0562dacb267f8556675762801514"},
{file = "setuptools-67.3.2.tar.gz", hash = "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012"}, {file = "setuptools-67.3.3.tar.gz", hash = "sha256:ed4e75fafe103c79b692f217158ba87edf38d31004b9dbc1913debb48793c828"},
] ]
[package.extras] [package.extras]
@@ -1651,14 +1450,14 @@ files = [
[[package]] [[package]]
name = "sphinx" name = "sphinx"
version = "4.3.2" version = "4.5.0"
description = "Python documentation generator" description = "Python documentation generator"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"},
{file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"},
] ]
[package.dependencies] [package.dependencies]
@@ -1667,11 +1466,11 @@ babel = ">=1.3"
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.14,<0.18" docutils = ">=0.14,<0.18"
imagesize = "*" imagesize = "*"
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
Jinja2 = ">=2.3" Jinja2 = ">=2.3"
packaging = "*" packaging = "*"
Pygments = ">=2.0" Pygments = ">=2.0"
requests = ">=2.5.0" requests = ">=2.5.0"
setuptools = "*"
snowballstemmer = ">=1.1" snowballstemmer = ">=1.1"
sphinxcontrib-applehelp = "*" sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*" sphinxcontrib-devhelp = "*"
@@ -1682,7 +1481,7 @@ sphinxcontrib-serializinghtml = ">=1.1.5"
[package.extras] [package.extras]
docs = ["sphinxcontrib-websupport"] 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"] test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"]
[[package]] [[package]]
@@ -1925,22 +1724,6 @@ files = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, {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]] [[package]]
name = "types-croniter" name = "types-croniter"
version = "1.3.2.5" version = "1.3.2.5"
@@ -1996,25 +1779,25 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.16.2" version = "20.19.0"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
files = [ files = [
{file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, {file = "virtualenv-20.19.0-py3-none-any.whl", hash = "sha256:54eb59e7352b573aa04d53f80fc9736ed0ad5143af445a1e539aada6eb947dd1"},
{file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, {file = "virtualenv-20.19.0.tar.gz", hash = "sha256:37a640ba82ed40b226599c522d411e4be5edb339a0c0de030c0dc7b646d61590"},
] ]
[package.dependencies] [package.dependencies]
distlib = ">=0.3.1,<1" distlib = ">=0.3.6,<1"
filelock = ">=3.2,<4" filelock = ">=3.4.1,<4"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""}
platformdirs = ">=2,<3" platformdirs = ">=2.4,<4"
[package.extras] [package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"]
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)"] 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]] [[package]]
name = "xdoctest" name = "xdoctest"
@@ -2048,14 +1831,14 @@ tests-strict = ["codecov (==2.0.15)", "pytest (==4.6.0)", "pytest (==4.6.0)", "p
[[package]] [[package]]
name = "zipp" name = "zipp"
version = "3.13.0" version = "3.14.0"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"}, {file = "zipp-3.14.0-py3-none-any.whl", hash = "sha256:188834565033387710d046e3fe96acfc9b5e86cbca7f39ff69cf21a4128198b7"},
{file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"}, {file = "zipp-3.14.0.tar.gz", hash = "sha256:9e5421e176ef5ab4c0ad896624e87a7b2f07aca746c9b2aa305952800cb8eecb"},
] ]
[package.extras] [package.extras]
@@ -2065,4 +1848,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.7" 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" croniter = "^1.1.0"
pytz = ">=2021.3,<2023.0" pytz = ">=2021.3,<2023.0"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^7.2.1" pytest = "^7.2.1"
coverage = {extras = ["toml"], version = "^7.1"} coverage = {extras = ["toml"], version = "^7.1"}
safety = "^2.3.5" safety = "^2.3.5"
mypy = "^1.0" mypy = "^1.0"
typeguard = "^2.13.2"
xdoctest = {extras = ["colors"], version = "^1.1.1"} xdoctest = {extras = ["colors"], version = "^1.1.1"}
sphinx = "^4.3.2" sphinx = "^4.3.2"
sphinx-autobuild = ">=2021.3.14" sphinx-autobuild = ">=2021.3.14"
pre-commit = "^2.21.0" 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" reorder-python-imports = "^3.9.0"
pre-commit-hooks = "^4.4.0" pre-commit-hooks = "^4.4.0"
Pygments = "^2.14.0"
pyupgrade = "^3.3.1" pyupgrade = "^3.3.1"
furo = ">=2021.11.12" furo = ">=2021.11.12"
pytest-cov = "^4.0.0" pytest-cov = "^4.0.0"
types-croniter = "^1.3.2" types-croniter = "^1.3.2"
types-pytz = "^2022.7.1" types-pytz = "^2022.7.1"
pytest_async = "^0.1.1"
pytest-asyncio = "^0.20.3" pytest-asyncio = "^0.20.3"
respx = "^0.20.1" respx = "^0.20.1"
pytest-mock = "^3.10.0" pytest-mock = "^3.10.0"
pytest-lazy-fixture = "^0.6.3" pytest-lazy-fixture = "^0.6.3"
pytest-xdist = "^3.2.0"
ruff = "^0.0.249"
bandit = "^1.7.4"
[tool.coverage.paths] [tool.coverage.paths]
source = ["src", "*/site-packages"] source = ["src", "*/site-packages"]
@@ -87,10 +79,9 @@ show_error_context = true
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"] requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry-dynamic-versioning] [tool.pytest.ini_options]
enable = true addopts = "-n 4 --ignore examples --cov=healthchecks_io --cov-report xml:.coverage.xml --cov-report=term-missing --cov-fail-under 91"
vcs = "git"
dirty = true
[tool.poetry-dynamic-versioning.substitution] [tool.ruff]
files = ["src/healthchecks_io/__init__.py"] line-length = 120
target-version = "py37"

View File

@@ -83,7 +83,8 @@ class AbstractClient(ABC):
endpoint (str): Endpoint to request endpoint (str): Endpoint to request
Raises: 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: Returns:
str: url for this str: url for this

View File

@@ -42,9 +42,7 @@ class AsyncClient(AbstractClient):
client (Optional[HTTPXAsyncClient], optional): A httpx.Asyncclient. If not client (Optional[HTTPXAsyncClient], optional): A httpx.Asyncclient. If not
passed in, one will be created for this object. Defaults to None. passed in, one will be created for this object. Defaults to None.
""" """
self._client: HTTPXAsyncClient = ( self._client: HTTPXAsyncClient = HTTPXAsyncClient() if client is None else client
HTTPXAsyncClient() if client is None else client
)
super().__init__( super().__init__(
api_key=api_key, api_key=api_key,
ping_key=ping_key, ping_key=ping_key,
@@ -53,9 +51,7 @@ class AsyncClient(AbstractClient):
api_version=api_version, api_version=api_version,
) )
self._client.headers["X-Api-Key"] = self._api_key self._client.headers["X-Api-Key"] = self._api_key
self._client.headers[ self._client.headers["user-agent"] = f"py-healthchecks.io-async/{client_version}"
"user-agent"
] = f"py-healthchecks.io-async/{client_version}"
self._client.headers["Content-type"] = "application/json" self._client.headers["Content-type"] = "application/json"
async def __aenter__(self) -> "AsyncClient": async def __aenter__(self) -> "AsyncClient":
@@ -97,9 +93,7 @@ class AsyncClient(AbstractClient):
Check: check that was just created Check: check that was just created
""" """
request_url = self._get_api_request_url("checks/") request_url = self._get_api_request_url("checks/")
response = self.check_response( response = self.check_response(await self._client.post(request_url, json=new_check.dict(exclude_none=True)))
await self._client.post(request_url, json=new_check.dict(exclude_none=True))
)
return Check.from_api_result(response.json()) return Check.from_api_result(response.json())
async def update_check(self, uuid: str, update_check: CheckCreate) -> Check: 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/") request_url = self._get_api_request_url("checks/")
if tags is not None: if tags is not None:
for tag in tags: for tag in tags:
request_url = self._add_url_params( request_url = self._add_url_params(request_url, {"tag": tag}, replace=False)
request_url, {"tag": tag}, replace=False
)
response = self.check_response(await self._client.get(request_url)) response = self.check_response(await self._client.get(request_url))
return [ return [Check.from_api_result(check_data) for check_data in response.json()["checks"]]
Check.from_api_result(check_data)
for check_data in response.json()["checks"]
]
async def get_check(self, check_id: str) -> Check: async def get_check(self, check_id: str) -> Check:
"""Get a single check by id. """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/") request_url = self._get_api_request_url(f"checks/{check_id}/pings/")
response = self.check_response(await self._client.get(request_url)) response = self.check_response(await self._client.get(request_url))
return [ return [CheckPings.from_api_result(check_data) for check_data in response.json()["pings"]]
CheckPings.from_api_result(check_data)
for check_data in response.json()["pings"]
]
async def get_check_flips( async def get_check_flips(
self, self,
@@ -274,8 +260,10 @@ class AsyncClient(AbstractClient):
Args: Args:
check_id (str): check uuid check_id (str): check uuid
seconds (Optional[int], optional): Returns the flips from the last value seconds. Defaults to None. 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. start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.. Defaults to None. Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.
Defaults to None.
Returns: Returns:
List[CheckStatuses]: List of status flips for this check List[CheckStatuses]: List of status flips for this check
@@ -307,10 +295,7 @@ class AsyncClient(AbstractClient):
""" """
request_url = self._get_api_request_url("channels/") request_url = self._get_api_request_url("channels/")
response = self.check_response(await self._client.get(request_url)) response = self.check_response(await self._client.get(request_url))
return [ return [Integration.from_api_result(integration_dict) for integration_dict in response.json()["channels"]]
Integration.from_api_result(integration_dict)
for integration_dict in response.json()["channels"]
]
async def get_badges(self) -> Dict[str, Badges]: async def get_badges(self) -> Dict[str, Badges]:
"""Returns a dict of all tags in the project, with badge URLs for each tag. """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. shields: returns JSON in a Shields.io compatible format.
In addition, badges have 2-state and 3-state variations: 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". svg3, json3, shields3: reports three states: "up", "late", and "down".
The response includes a special * entry: this pseudo-tag reports the overal status 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/") request_url = self._get_api_request_url("badges/")
response = self.check_response(await self._client.get(request_url)) response = self.check_response(await self._client.get(request_url))
return { return {key: Badges.from_api_result(item) for key, item in response.json()["badges"].items()}
key: Badges.from_api_result(item)
for key, item in response.json()["badges"].items()
}
async def success_ping( async def success_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that a job has completed successfully. """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. 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 Tuple[bool, str]: success (true or false) and the response text
""" """
ping_url = self._get_ping_url(uuid, slug, "") ping_url = self._get_ping_url(uuid, slug, "")
response = self.check_ping_response( response = self.check_ping_response(await self._client.post(ping_url, content=data))
await self._client.post(ping_url, content=data)
)
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
async def start_ping( async def start_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Sends a "job has started!" message to Healthchecks.io. """Sends a "job has started!" message to Healthchecks.io.
Sending a "start" signal is optional, but it enables a few extra features: 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 Tuple[bool, str]: success (true or false) and the response text
""" """
ping_url = self._get_ping_url(uuid, slug, "/start") ping_url = self._get_ping_url(uuid, slug, "/start")
response = self.check_ping_response( response = self.check_ping_response(await self._client.post(ping_url, content=data))
await self._client.post(ping_url, content=data)
)
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
async def fail_ping( async def fail_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed. """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. 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 Tuple[bool, str]: success (true or false) and the response text
""" """
ping_url = self._get_ping_url(uuid, slug, "/fail") ping_url = self._get_ping_url(uuid, slug, "/fail")
response = self.check_ping_response( response = self.check_ping_response(await self._client.post(ping_url, content=data))
await self._client.post(ping_url, content=data)
)
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
async def exit_code_ping( async def exit_code_ping(self, exit_code: int, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, exit_code: int, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed. """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. 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 Tuple[bool, str]: success (true or false) and the response text
""" """
ping_url = self._get_ping_url(uuid, slug, f"/{exit_code}") ping_url = self._get_ping_url(uuid, slug, f"/{exit_code}")
response = self.check_ping_response( response = self.check_ping_response(await self._client.post(ping_url, content=data))
await self._client.post(ping_url, content=data)
)
return (True if response.status_code == 200 else False, response.text) 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/") request_url = self._get_api_request_url("checks/")
if tags is not None: if tags is not None:
for tag in tags: for tag in tags:
request_url = self._add_url_params( request_url = self._add_url_params(request_url, {"tag": tag}, replace=False)
request_url, {"tag": tag}, replace=False
)
response = self.check_response(self._client.get(request_url)) response = self.check_response(self._client.get(request_url))
return [ return [checks.Check.from_api_result(check_data) for check_data in response.json()["checks"]]
checks.Check.from_api_result(check_data)
for check_data in response.json()["checks"]
]
def get_check(self, check_id: str) -> checks.Check: def get_check(self, check_id: str) -> checks.Check:
"""Get a single check by id. """Get a single check by id.
@@ -137,9 +132,7 @@ class Client(AbstractClient):
Check: check that was just created Check: check that was just created
""" """
request_url = self._get_api_request_url("checks/") request_url = self._get_api_request_url("checks/")
response = self.check_response( response = self.check_response(self._client.post(request_url, json=new_check.dict(exclude_none=True)))
self._client.post(request_url, json=new_check.dict(exclude_none=True))
)
return Check.from_api_result(response.json()) return Check.from_api_result(response.json())
def update_check(self, uuid: str, update_check: CheckCreate) -> Check: 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/") request_url = self._get_api_request_url(f"checks/{check_id}/pings/")
response = self.check_response(self._client.get(request_url)) response = self.check_response(self._client.get(request_url))
return [ return [checks.CheckPings.from_api_result(check_data) for check_data in response.json()["pings"]]
checks.CheckPings.from_api_result(check_data)
for check_data in response.json()["pings"]
]
def get_check_flips( def get_check_flips(
self, self,
@@ -261,8 +251,10 @@ class Client(AbstractClient):
Args: Args:
check_id (str): check uuid check_id (str): check uuid
seconds (Optional[int], optional): Returns the flips from the last value seconds. Defaults to None. 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. start (Optional[int], optional): Returns flips that are newer than the specified UNIX timestamp.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.. Defaults to None. Defaults to None.
end (Optional[int], optional): Returns flips that are older than the specified UNIX timestamp.
Defaults to None.
Returns: Returns:
List[checks.CheckStatuses]: List of status flips for this check 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. shields: returns JSON in a Shields.io compatible format.
In addition, badges have 2-state and 3-state variations: 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". svg3, json3, shields3: reports three states: "up", "late", and "down".
The response includes a special * entry: this pseudo-tag reports the overal status 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/") request_url = self._get_api_request_url("badges/")
response = self.check_response(self._client.get(request_url)) response = self.check_response(self._client.get(request_url))
return { return {key: badges.Badges.from_api_result(item) for key, item in response.json()["badges"].items()}
key: badges.Badges.from_api_result(item)
for key, item in response.json()["badges"].items()
}
def success_ping( def success_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that a job has completed successfully. """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. 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)) response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
def start_ping( def start_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Sends a "job has started!" message to Healthchecks.io. """Sends a "job has started!" message to Healthchecks.io.
Sending a "start" signal is optional, but it enables a few extra features: 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)) response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
def fail_ping( def fail_ping(self, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed. """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. 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)) response = self.check_ping_response(self._client.post(ping_url, content=data))
return (True if response.status_code == 200 else False, response.text) return (True if response.status_code == 200 else False, response.text)
def exit_code_ping( def exit_code_ping(self, exit_code: int, uuid: str = "", slug: str = "", data: str = "") -> Tuple[bool, str]:
self, exit_code: int, uuid: str = "", slug: str = "", data: str = ""
) -> Tuple[bool, str]:
"""Signals to Healthchecks.io that the job has failed. """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. 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] uuid: Optional[str]
@validator("uuid", always=True) @validator("uuid", always=True)
def validate_uuid( def validate_uuid(cls, value: Optional[str], values: Dict[str, Any]) -> Optional[str]: # noqa: B902
cls, value: Optional[str], values: Dict[str, Any] # noqa: B902
) -> Optional[str]:
"""Tries to set the uuid from the ping_url. """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 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: 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 # Parse the url, grab the path and then just get the name using pathlib
path = PurePath(str(urlparse(values.get("ping_url")).path)) path = PurePath(str(urlparse(values.get("ping_url")).path))
return path.name return path.name
@@ -66,9 +65,7 @@ class CheckCreate(BaseModel):
"""Pydantic object for creating a check.""" """Pydantic object for creating a check."""
name: Optional[str] = Field("", description="Name of the check") name: Optional[str] = Field("", description="Name of the check")
tags: Optional[str] = Field( tags: Optional[str] = Field("", description="String separated list of tags to apply")
"", description="String separated list of tags to apply"
)
desc: Optional[str] = Field("", description="Description of the check") desc: Optional[str] = Field("", description="Description of the check")
timeout: Optional[int] = Field( timeout: Optional[int] = Field(
86400, 86400,
@@ -147,9 +144,7 @@ class CheckCreate(BaseModel):
def validate_methods(cls, value: str) -> str: def validate_methods(cls, value: str) -> str:
"""Validate that methods.""" """Validate that methods."""
if value not in ("", "POST"): if value not in ("", "POST"):
raise ValueError( raise ValueError("Methods is invalid, it should be either an empty string or POST")
"Methods is invalid, it should be either an empty string or POST"
)
return value return value
@validator("unique") @validator("unique")
@@ -167,9 +162,7 @@ class CheckUpdate(CheckCreate):
"""Pydantic object for updating a check.""" """Pydantic object for updating a check."""
name: Optional[str] = Field(None, description="Name of the check") name: Optional[str] = Field(None, description="Name of the check")
tags: Optional[str] = Field( tags: Optional[str] = Field(None, description="String separated list of tags to apply")
None, description="String separated list of tags to apply"
)
timeout: Optional[int] = Field( timeout: Optional[int] = Field(
None, None,
description="The expected period of this check in seconds.", description="The expected period of this check in seconds.",
@@ -243,9 +236,7 @@ class CheckPings(BaseModel):
duration: Optional[float] = None duration: Optional[float] = None
@classmethod @classmethod
def from_api_result( def from_api_result(cls, ping_dict: Dict[str, Union[str, int, datetime]]) -> "CheckPings":
cls, ping_dict: Dict[str, Union[str, int, datetime]]
) -> "CheckPings":
"""Converts a dictionary from the healthchecks api into a CheckPings object.""" """Converts a dictionary from the healthchecks api into a CheckPings object."""
ping_dict["number_of_pings"] = ping_dict["n"] ping_dict["number_of_pings"] = ping_dict["n"]
ping_dict["user_agent"] = ping_dict["ua"] 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 HCAPIError
from healthchecks_io import HCAPIRateLimitError from healthchecks_io import HCAPIRateLimitError
from healthchecks_io import NonUniqueSlugError from healthchecks_io import NonUniqueSlugError
from healthchecks_io.client._abstract import AbstractClient
def test_abstract_add_url_params(test_abstract_client): def test_abstract_add_url_params(test_abstract_client):

View File

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

View File

@@ -1,13 +1,9 @@
from urllib.parse import urljoin from urllib.parse import urljoin
import pytest import pytest
import respx
from httpx import Client as HTTPXClient
from httpx import Response from httpx import Response
from healthchecks_io import CheckCreate
from healthchecks_io import CheckTrap from healthchecks_io import CheckTrap
from healthchecks_io import CheckUpdate
from healthchecks_io import PingFailedError from healthchecks_io import PingFailedError
from healthchecks_io import WrongClientError 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") success_url = urljoin(test_client._ping_url, "test")
respx_mock.post(success_url).mock(return_value=Response(status_code=200, text="OK")) 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 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") start_url = urljoin(test_client._ping_url, "test/start")
respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK")) respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK"))
with pytest.raises(PingFailedError): with pytest.raises(PingFailedError):
with CheckTrap(test_client, uuid="test") as ct: with CheckTrap(test_client, uuid="test"):
pass pass
@@ -39,7 +35,7 @@ def test_check_trap_sync_exception(respx_mock, test_client):
fail_url = urljoin(test_client._ping_url, "test/fail") fail_url = urljoin(test_client._ping_url, "test/fail")
respx_mock.post(fail_url).mock(return_value=Response(status_code=200, text="OK")) respx_mock.post(fail_url).mock(return_value=Response(status_code=200, text="OK"))
with pytest.raises(Exception): with pytest.raises(Exception):
with CheckTrap(test_client, uuid="test") as ct: with CheckTrap(test_client, uuid="test"):
raise Exception("Exception") 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") success_url = urljoin(test_async_client._ping_url, "test")
respx_mock.post(success_url).mock(return_value=Response(status_code=200, text="OK")) 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 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") start_url = urljoin(test_async_client._ping_url, "test/start")
respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK")) respx_mock.post(start_url).mock(return_value=Response(status_code=444, text="OK"))
with pytest.raises(PingFailedError): with pytest.raises(PingFailedError):
async with CheckTrap(test_async_client, uuid="test") as ct: async with CheckTrap(test_async_client, uuid="test"):
pass 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")) respx_mock.post(fail_url).mock(return_value=Response(status_code=200, text="OK"))
with pytest.raises(Exception): 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") 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): async def test_check_trap_wrong_client_error(test_client, test_async_client):
with pytest.raises(WrongClientError): with pytest.raises(WrongClientError):
async with CheckTrap(test_client, uuid="test") as ct: async with CheckTrap(test_client, uuid="test"):
pass pass
with pytest.raises(WrongClientError): with pytest.raises(WrongClientError):
with CheckTrap(test_async_client, uuid="test") as ct: with CheckTrap(test_async_client, uuid="test"):
pass pass

View File

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