diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 74afb5d..9375bb8 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -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 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 50aa630..2e59098 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff4a505..71d0380 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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. diff --git a/bandit.yml b/bandit.yml new file mode 100644 index 0000000..82f9ff9 --- /dev/null +++ b/bandit.yml @@ -0,0 +1,3 @@ +exclude_dirs: ["test", "examples"] +assert_used: + skips: ["*/test_*.py", "noxfile.py"] diff --git a/noxfile.py b/noxfile.py index eddd01b..a6fa464 100644 --- a/noxfile.py +++ b/noxfile.py @@ -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 /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) diff --git a/poetry.lock b/poetry.lock index fdae160..d5908ab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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" diff --git a/pyproject.toml b/pyproject.toml index 163672c..0bd0101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/src/healthchecks_io/client/_abstract.py b/src/healthchecks_io/client/_abstract.py index a4b0d42..3854277 100644 --- a/src/healthchecks_io/client/_abstract.py +++ b/src/healthchecks_io/client/_abstract.py @@ -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 diff --git a/src/healthchecks_io/client/async_client.py b/src/healthchecks_io/client/async_client.py index 3a18ad6..6a7c256 100644 --- a/src/healthchecks_io/client/async_client.py +++ b/src/healthchecks_io/client/async_client.py @@ -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) diff --git a/src/healthchecks_io/client/sync_client.py b/src/healthchecks_io/client/sync_client.py index bd09d92..16d0f3f 100644 --- a/src/healthchecks_io/client/sync_client.py +++ b/src/healthchecks_io/client/sync_client.py @@ -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. diff --git a/src/healthchecks_io/schemas/checks.py b/src/healthchecks_io/schemas/checks.py index fccfb7f..e6eede2 100644 --- a/src/healthchecks_io/schemas/checks.py +++ b/src/healthchecks_io/schemas/checks.py @@ -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"] diff --git a/tests/client/test_abstract.py b/tests/client/test_abstract.py index c444f5d..bd39a7e 100644 --- a/tests/client/test_abstract.py +++ b/tests/client/test_abstract.py @@ -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): diff --git a/tests/client/test_async.py b/tests/client/test_async.py index 79e3dce..54e6f86 100644 --- a/tests/client/test_async.py +++ b/tests/client/test_async.py @@ -1,7 +1,6 @@ from urllib.parse import urljoin import pytest -import respx from httpx import AsyncClient as HTTPXAsyncClient from httpx import Response diff --git a/tests/client/test_check_trap.py b/tests/client/test_check_trap.py index 9a640f9..5876dcd 100644 --- a/tests/client/test_check_trap.py +++ b/tests/client/test_check_trap.py @@ -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 diff --git a/tests/client/test_sync.py b/tests/client/test_sync.py index e5da7ca..f7e82ef 100644 --- a/tests/client/test_sync.py +++ b/tests/client/test_sync.py @@ -1,7 +1,6 @@ from urllib.parse import urljoin import pytest -import respx from httpx import Client as HTTPXClient from httpx import Response