diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..52c47f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,28 @@ +.git +.gitignore +.venv +venv +__pycache__ +*.pyc +*.pyo +.env +.env.* +*.egg-info +dist +build +node_modules +.mypy_cache +.pytest_cache +.ruff_cache +*.md +!README.md +Dockerfile +docker-compose*.yml +.dockerignore +.github +tests +*.toml +!pyproject.toml +.gitignore +AGENTS.md +LICENSE diff --git a/.gitignore b/.gitignore index fb484cc..95242d4 100644 --- a/.gitignore +++ b/.gitignore @@ -73,9 +73,6 @@ target/ # Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version - # celery beat schedule file celerybeat-schedule @@ -85,6 +82,7 @@ celerybeat-schedule # Environments .env .venv +uv.lock env/ venv/ ENV/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ac0dfc9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: python -dist: xenial -cache: pip -python: - - 3.6 - - 3.7 -stages: - - test - - deploy -before_script: - - pip install -U poetry tox-travis codecov -script: - - tox -jobs: - include: - - stage: deploy - python: 3.7 - script: skip - before_install: - - pip install 'poetry==1.1.7' - install: - - poetry install - before_deploy: - - poetry build - deploy: - provider: script - script: poetry publish - skip_cleanup: true - on: - tags: true - after_success: - - codecov diff --git a/Dockerfile b/Dockerfile index 894bfee..388c762 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,42 +1,80 @@ -# This Dockerfile is used for standalone installs. +# ============================================================ +# Stage 1: Builder — install dependencies in an isolated layer +# ============================================================ +FROM python:3.14-slim-bookworm AS builder + +# Install uv for ultra-fast dependency resolution +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +WORKDIR /app + +# Copy only dependency manifests first (layer cache optimization) +COPY pyproject.toml uv.lock ./ + +# Install dependencies into the system Python (no venv needed in container) +RUN uv sync --frozen --no-dev --no-install-project + +# Copy application code +COPY . . + +# Install the project itself +RUN uv sync --frozen --no-dev + +# ============================================================ +# Stage 2: Go Builder — build test clients +# ============================================================ +FROM golang:1.23-bookworm AS go-builder + +RUN apt-get update && apt-get install -y git -# Build ndt7, ndt5 and dash Go clients. -FROM golang:1.17-bullseye AS build -RUN apt-get update -RUN apt-get install -y git ENV GO111MODULE=on -RUN go get github.com/neubot/dash/cmd/dash-client@master -RUN go get github.com/m-lab/ndt7-client-go/cmd/ndt7-client -RUN go get github.com/m-lab/ndt5-client-go/cmd/ndt5-client - -# Murakami image -FROM python:3.7-bullseye -# Install dependencies, speedtest and ooniprobe. -# For ooniprobe, see https://ooni.org/install/cli/ubuntu-debian for instructions -RUN apt-key adv --verbose --keyserver hkp://keyserver.ubuntu.com --recv-keys 'B5A08F01796E7F521861B449372D1FF271F2DD50' -RUN echo "deb http://deb.ooni.org/ unstable main" | tee /etc/apt/sources.list.d/ooniprobe.list -RUN apt-get update -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -yq git gcc libc-dev libffi-dev libssl-dev make rustc cargo ooniprobe-cli curl -RUN /usr/local/bin/python3.7 -m pip install --upgrade pip -RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash -RUN apt-get install speedtest -RUN pip install 'poetry==1.1.7' - -WORKDIR /murakami - -# Copy Murakami and previously built test clients into the container. -COPY . /murakami/ -COPY --from=build /go/bin/* /murakami/bin/ - -COPY ./configs/speedtest-cli.json /root/.config/ookla/ - -# Set up poetry to not create a virtualenv, since the docker container is -# isolated already, and install the required dependencies. -RUN poetry config virtualenvs.create false \ - && poetry install --no-dev --no-interaction - -# Add binaries' path to PATH. -ENV PATH="/murakami/bin:${PATH}" - -ENTRYPOINT [ "python", "-m", "murakami" ] + +RUN go install github.com/neubot/dash/cmd/dash-client@master && \ + go install github.com/m-lab/ndt7-client-go/cmd/ndt7-client && \ + go install github.com/m-lab/ndt5-client-go/cmd/ndt5-client + +# ============================================================ +# Stage 3: Runtime — minimal image with only what's needed +# ============================================================ +FROM python:3.14-slim-bookworm AS runtime + +# Install system dependencies +RUN apt-key adv --verbose --keyserver hkp://keyserver.ubuntu.com --recv-keys 'B5A08F01796E7F521861B449372D1FF271F2DD50' && \ + echo "deb http://deb.ooni.org/ unstable main" | tee /etc/apt/sources.list.d/ooniprobe.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + git gcc libc-dev libffi-dev libssl-dev make rustc cargo curl \ + ooniprobe-cli && \ + curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash && \ + apt-get install -y speedtest && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Security: run as non-root +RUN groupadd --gid 1000 appuser && \ + useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser + +WORKDIR /app + +# Copy the virtual environment from builder +COPY --from=builder /app/.venv /app/.venv + +# Copy Go binaries +COPY --from=go-builder /go/bin /app/bin + +# Copy application code +COPY --from=builder /app . + +# Copy speedtest config +COPY ./configs/speedtest-cli.json /home/appuser/.config/ookla/ + +# Put the venv on PATH +ENV PATH="/app/.venv/bin:/app/bin:${PATH}" + +# Add binaries' path to PATH +ENV PYTHONPATH="/app" + +# Drop privileges +USER appuser + +ENTRYPOINT ["python", "-m", "murakami"] diff --git a/Dockerfile.template b/Dockerfile.template deleted file mode 100644 index 8a77a34..0000000 --- a/Dockerfile.template +++ /dev/null @@ -1,43 +0,0 @@ -# This Dockerfile is used to build the image for a BalenaOS device. The -# %%BALENA_MACHINE_NAME%% variable is replaced with the name of the device. - -# Build ndt7, ndt5 and dash Go clients. -FROM balenalib/%%BALENA_MACHINE_NAME%%-debian-golang:1.15-bullseye-build AS build -RUN apt-get update -RUN apt-get install -y git -ENV GO111MODULE=on -RUN go get github.com/neubot/dash/cmd/dash-client@master -RUN go get github.com/m-lab/ndt7-client-go/cmd/ndt7-client -RUN go get github.com/m-lab/ndt5-client-go/cmd/ndt5-client - -# Murakami image -FROM balenalib/%%BALENA_MACHINE_NAME%%-debian-python:3.7-bullseye -# Install dependencies, speedtest, and ooniprobe -# For ooniprobe, see https://ooni.org/install/cli/ubuntu-debian for instructions -RUN apt-key adv --verbose --keyserver hkp://keyserver.ubuntu.com --recv-keys 'B5A08F01796E7F521861B449372D1FF271F2DD50' -RUN echo "deb http://deb.ooni.org/ unstable main" | tee /etc/apt/sources.list.d/ooniprobe.list -RUN apt-get update -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -yq git gcc libc-dev libffi-dev libssl-dev make rustc cargo ooniprobe-cli curl -RUN /usr/local/bin/python3.7 -m pip install --upgrade pip -RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash -RUN apt-get install speedtest -RUN pip install 'poetry==1.1.7' - -WORKDIR /murakami - -# Copy Murakami and previously built test clients into the container. -COPY . /murakami/ -COPY --from=build /go/bin/* /murakami/bin/ - -COPY ./configs/speedtest-cli.json /root/.config/ookla/ - -# Set up poetry to not create a virtualenv, since the docker container is -# isolated already, and install the required dependencies. -RUN poetry config virtualenvs.create false \ - && poetry install --no-dev --no-interaction - -# Add binaries' path to PATH. -ENV PATH="/murakami/bin:${PATH}" - -ENTRYPOINT [ "murakami", "-d", "/data/config.json" ] diff --git a/murakami/__init__.py b/murakami/__init__.py index 1b0f74a..6a3a990 100644 --- a/murakami/__init__.py +++ b/murakami/__init__.py @@ -6,4 +6,5 @@ one or more remote servers via SCP, or to a Google Cloud Storage bucket. Results are saved as individual files in JSON new line format (.jsonl). """ + __version__ = "0.1.0" diff --git a/murakami/__main__.py b/murakami/__main__.py index 65ca7b2..a568cfb 100644 --- a/murakami/__main__.py +++ b/murakami/__main__.py @@ -6,11 +6,11 @@ one or more remote servers via SCP, or to a Google Cloud Storage bucket. Results are saved as individual files in JSON new line format (.jsonl). """ -from collections import ChainMap, OrderedDict -from collections.abc import Mapping + import logging import os import signal +from collections import ChainMap, OrderedDict import configargparse import livejson @@ -30,8 +30,11 @@ def load_env(): It ignores variables starting with MURAKAMI_SETTINGS as those are read and managed by configargparse.""" acc = {} - env = {k: v for k, v in os.environ.items() if k.startswith('MURAKAMI_') and - not k.startswith('MURAKAMI_SETTINGS')} + env = { + k: v + for k, v in os.environ.items() + if k.startswith("MURAKAMI_") and not k.startswith("MURAKAMI_SETTINGS") + } def recurse(sec, value, acc): key = sec.pop(0) @@ -41,14 +44,16 @@ def recurse(sec, value, acc): acc[key] = value for k, v in env.items(): - _, *sec = k.lower().split('_', maxsplit=3) + _, *sec = k.lower().split("_", maxsplit=3) recurse(sec, v, acc) return acc + def default_device_id(): """Return the value of the environment variable BALENA_DEVICE_ID if set, or an empty string.""" - return os.environ.get('BALENA_DEVICE_UUID', "") + return os.environ.get("BALENA_DEVICE_UUID", "") + class TomlConfigFileParser(configargparse.ConfigFileParser): """ @@ -56,10 +61,13 @@ class TomlConfigFileParser(configargparse.ConfigFileParser): and then merges matching environment variables, and then puts then saves the result while passing back just the settings portion to configargparse. """ + def get_syntax_description(self): """Returns a description of the file format parsed by the class.""" - msg = ("Parses a TOML-format configuration file " - "(see https://github.com/toml-lang/toml for the spec).") + msg = ( + "Parses a TOML-format configuration file " + "(see https://github.com/toml-lang/toml for the spec)." + ) return msg def parse(self, stream): @@ -86,7 +94,7 @@ def parse(self, stream): def main(): - """ The main function for Murakami.""" + """The main function for Murakami.""" parser = configargparse.ArgParser( auto_env_var_prefix="murakami_settings_", config_file_parser_class=TomlConfigFileParser, @@ -106,8 +114,9 @@ def main(): "--dynamic-state", default=defaults.DYNAMIC_FILE, dest="dynamic", - help= - "Path to dynamic configuration store, used to override settings via Webthings (default:" + defaults.DYNAMIC_FILE + ").", + help="Path to dynamic configuration store, used to override settings via Webthings (default:" + + defaults.DYNAMIC_FILE + + ").", ) parser.add( "-p", @@ -116,9 +125,9 @@ def main(): default=defaults.HTTP_PORT, help="The port to listen on for incoming connections (default: 80).", ) - parser.add("-n", - "--hostname", - help="The mDNS hostname for WebThings (default: automatic).") + parser.add( + "-n", "--hostname", help="The mDNS hostname for WebThings (default: automatic)." + ) parser.add( "-s", "--ssl-options", diff --git a/murakami/defaults.py b/murakami/defaults.py index 6a870f4..f56a321 100644 --- a/murakami/defaults.py +++ b/murakami/defaults.py @@ -1,12 +1,11 @@ """ Common default values for Murakami. """ + SSH_PORT = 22 SSH_TIMEOUT = 5 HTTP_PORT = 80 TESTS_PER_DAY = 4 EXPORT_PATH = "/var/cache/murakami" DYNAMIC_FILE = "/var/lib/murakami/config.json" -CONFIG_FILES = [ - "/etc/murakami/murakami.toml", "~/.config/murakami/murakami.toml" -] +CONFIG_FILES = ["/etc/murakami/murakami.toml", "~/.config/murakami/murakami.toml"] diff --git a/murakami/errors.py b/murakami/errors.py index a87b99b..c21f1f6 100644 --- a/murakami/errors.py +++ b/murakami/errors.py @@ -12,6 +12,7 @@ class ExporterError(Error): name -- The name of the runner message -- The error message """ + def __init__(self, name, message): super().__init__() self.name = name @@ -25,10 +26,11 @@ class RunnerError(Error): name -- The name of the runner message -- The error message """ + def __init__(self, name, message): super().__init__() self.name = name self.message = message - + def __str__(self): return self.message diff --git a/murakami/exporter.py b/murakami/exporter.py index 5b1a10e..91a2204 100644 --- a/murakami/exporter.py +++ b/murakami/exporter.py @@ -2,13 +2,15 @@ This module includes the wrapper for all result exporters, which defines their interface. """ -import logging +import logging from datetime import datetime + from murakami.errors import ExporterError logger = logging.getLogger(__name__) + class MurakamiExporter: """ MurakamiRunner is the superclass of all test runner plugins, and largely @@ -23,13 +25,14 @@ class MurakamiExporter: * `config`: A configuration dictionary passed to this instance from MurakamiServer """ + def __init__( - self, - name="", - location=None, - network_type=None, - connection_type=None, - config=None, + self, + name="", + location=None, + network_type=None, + connection_type=None, + config=None, ): self.name = name self._location = location @@ -50,8 +53,7 @@ def push(self, test_name="", data=None, timestamp=None): else: return self._push_single(test_name, data, timestamp) - def _push_single(self, test_name="", data=None, timestamp=None, - test_idx=None): + def _push_single(self, test_name="", data=None, timestamp=None, test_idx=None): """ Push results to this exporter (must be implemented by all exporters). @@ -62,20 +64,21 @@ def _push_single(self, test_name="", data=None, timestamp=None, """ raise ExporterError(self.name, "No push() function implemented.") - def _generate_filename(self, test_name="", timestamp=None, - test_idx=None): + def _generate_filename(self, test_name="", timestamp=None, test_idx=None): if timestamp is None: timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f") - + # If the result is part of a set of tests coming from a single runner, # these tests will have the same timestamp. To distinguish them and not - # overwrite the same file N times, we append test_idx to the filename. + # overwrite the same file N times, we append test_idx to the filename. if test_idx is not None: timestamp = "%d-%s" % (test_idx, timestamp) - - if (self._location is not None and self._network_type is not None - and self._connection_type is not None): + if ( + self._location is not None + and self._network_type is not None + and self._connection_type is not None + ): return "%s-%s-%s-%s-%s.jsonl" % ( test_name.lower(), self._location, diff --git a/murakami/exporters/gcs.py b/murakami/exporters/gcs.py index 74db067..c4ac111 100644 --- a/murakami/exporters/gcs.py +++ b/murakami/exporters/gcs.py @@ -1,10 +1,7 @@ -import os -import io -import subprocess import logging -import jsonlines from google.cloud import storage + from murakami.exporter import MurakamiExporter logger = logging.getLogger(__name__) @@ -13,13 +10,14 @@ class GCSExporter(MurakamiExporter): """This exporter allows to upload data to a Google Cloud Storage bucket.""" + def __init__( - self, - name="", - location=None, - network_type=None, - connection_type=None, - config=None, + self, + name="", + location=None, + network_type=None, + connection_type=None, + config=None, ): super().__init__( name=name, @@ -43,8 +41,7 @@ def upload(self, data, bucket_name, object_name): blob = storage.Blob(object_name, bucket) blob.upload_from_string(data) - def _push_single(self, test_name="", data=None, timestamp=None, - test_idx=None): + def _push_single(self, test_name="", data=None, timestamp=None, test_idx=None): """Upload the test data to GCS using the provided configuration.""" if self.target is None: logger.error("GCS: target must be provided.") @@ -57,19 +54,20 @@ def _push_single(self, test_name="", data=None, timestamp=None, # path. e.g. gs://bucket/path/to/results becomes: # - bucket_name: bucket # - path: path/to/results - t = self.target.split('/') + t = self.target.split("/") bucket_name = t[2] - object_name = '' + object_name = "" if len(t) > 3: - object_name = '/'.join(t[3:]) - if t[3] != '': - object_name += '/' + object_name = "/".join(t[3:]) + if t[3] != "": + object_name += "/" object_name += test_filename - logger.info("Uploading test data - Bucket: %s, Object: %s", - bucket_name, object_name) + logger.info( + "Uploading test data - Bucket: %s, Object: %s", bucket_name, object_name + ) self.upload(data, bucket_name, object_name) except ValueError as e: - logger.error('Error while uploading to GCS: %s', e) \ No newline at end of file + logger.error("Error while uploading to GCS: %s", e) diff --git a/murakami/exporters/http.py b/murakami/exporters/http.py index 7924af5..4f22b86 100644 --- a/murakami/exporters/http.py +++ b/murakami/exporters/http.py @@ -1,6 +1,5 @@ -import logging -import os import json +import logging import requests @@ -30,8 +29,7 @@ def __init__( logging.debug(config) self._url = config.get("url") - def _push_single(self, test_name="", data=None, timestamp=None, - test_idx=None): + def _push_single(self, test_name="", data=None, timestamp=None, test_idx=None): # Make a JSON payload with the expected format data = json.loads(data) data = {k: v for k, v in data.items() if v is not None} @@ -39,13 +37,11 @@ def _push_single(self, test_name="", data=None, timestamp=None, # POST the payload. resp = requests.post(self._url, json=json_data) - logging.debug("Exporting data: {}".format(json.dumps(json_data))) + logging.debug(f"Exporting data: {json.dumps(json_data)}") if not resp.ok: - logger.error("Exporting to HTTP endpoint failed: {}".format(resp.json())) + logger.error(f"Exporting to HTTP endpoint failed: {resp.json()}") return False logger.info( - "Test data successfully sent to {}. Response: {}".format( - self._url, resp.json() - ) + f"Test data successfully sent to {self._url}. Response: {resp.json()}" ) return True diff --git a/murakami/exporters/local.py b/murakami/exporters/local.py index 49ae5ba..e106eb2 100644 --- a/murakami/exporters/local.py +++ b/murakami/exporters/local.py @@ -9,13 +9,14 @@ class LocalExporter(MurakamiExporter): """This exporter saves data to a local directory.""" + def __init__( - self, - name="", - location=None, - network_type=None, - connection_type=None, - config=None, + self, + name="", + location=None, + network_type=None, + connection_type=None, + config=None, ): super().__init__( name=name, @@ -27,11 +28,11 @@ def __init__( logging.debug(config) self._path = config.get("path", defaults.EXPORT_PATH) - def _push_single(self, test_name="", data=None, timestamp=None, - test_idx=None): + def _push_single(self, test_name="", data=None, timestamp=None, test_idx=None): try: dst_path = os.path.join( - self._path, self._generate_filename(test_name, timestamp, test_idx)) + self._path, self._generate_filename(test_name, timestamp, test_idx) + ) logger.info("Copying data to %s", dst_path) with open(dst_path, "w") as output: output.write(data) diff --git a/murakami/exporters/scp.py b/murakami/exporters/scp.py index 973a07a..b52946a 100644 --- a/murakami/exporters/scp.py +++ b/murakami/exporters/scp.py @@ -2,7 +2,6 @@ import logging import os -import jsonlines from paramiko import SSHClient from paramiko.client import AutoAddPolicy from scp import SCPClient @@ -16,13 +15,14 @@ class SCPExporter(MurakamiExporter): """This exporter allows to copy Murakami's data path to a remote host and folder via SCP.""" + def __init__( - self, - name="", - location=None, - network_type=None, - connection_type=None, - config=None, + self, + name="", + location=None, + network_type=None, + connection_type=None, + config=None, ): # Check if a configuration for the SCP exporter has been provided. super().__init__( @@ -39,8 +39,7 @@ def __init__( self.password = config.get("password", None) self.private_key = config.get("key", None) - def _push_single(self, test_name="", data=None, timestamp=None, - test_idx=None): + def _push_single(self, test_name="", data=None, timestamp=None, test_idx=None): """Copy the files over SCP using the provided configuration.""" if self.target is None: logger.error("scp.target must be specified") diff --git a/murakami/runner.py b/murakami/runner.py index bcef865..1c05476 100644 --- a/murakami/runner.py +++ b/murakami/runner.py @@ -2,13 +2,12 @@ This module includes the wrapper for all test runners, which defines their interface. """ -from datetime import datetime -import logging -from webthing import Thing +import logging +from datetime import datetime -from murakami.errors import RunnerError import murakami.utils as utils +from murakami.errors import RunnerError _logger = logging.getLogger(__name__) @@ -25,9 +24,18 @@ class MurakamiRunner: MurakamiServer * `data_cb`: The callback function that receives the test results """ - def __init__(self, title, description="", config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + title, + description="", + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): self.title = title self.description = description self._config = config @@ -46,15 +54,12 @@ def start_test(self): timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f") data = self._start_test() if self._data_cb is not None: - self._data_cb(test_name=self.title, - data=data, - timestamp=timestamp) + self._data_cb(test_name=self.title, data=data, timestamp=timestamp) return data logging.info("Test runner %s disabled, skipping.", self.title) def _stop_test(self): - _logger.debug("No special handling needed for stopping runner %s", - self.title) + _logger.debug("No special handling needed for stopping runner %s", self.title) def stop_test(self): """Stops this test, wraps the actual stop function (optional).""" diff --git a/murakami/runners/dash.py b/murakami/runners/dash.py index 1e90450..1e8867a 100644 --- a/murakami/runners/dash.py +++ b/murakami/runners/dash.py @@ -1,9 +1,6 @@ import logging import shutil import subprocess -import uuid - -import jsonlines from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -13,9 +10,16 @@ class DashClient(MurakamiRunner): """Run Dash tests.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="DASH", description="The Neubot DASH network test.", @@ -31,14 +35,13 @@ def __init__(self, config=None, data_cb=None, def _start_test(): logger.info("Starting DASH test...") if shutil.which("dash-client") is not None: - output = subprocess.run(["dash-client"], - check=True, - text=True, - capture_output=True) + output = subprocess.run( + ["dash-client"], check=True, text=True, capture_output=True + ) logger.info("Dash test complete.") # TODO: write parser. Only print the last line for now. return output.stdout.splitlines()[-1] else: raise RunnerError( - "dash", - "Executable dash-client does not exist, please install DASH.") + "dash", "Executable dash-client does not exist, please install DASH." + ) diff --git a/murakami/runners/ndt5.py b/murakami/runners/ndt5.py index 8e61e8c..5862f32 100644 --- a/murakami/runners/ndt5.py +++ b/murakami/runners/ndt5.py @@ -1,9 +1,8 @@ +import datetime +import json import logging import shutil import subprocess -import uuid -import datetime -import json from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -13,9 +12,16 @@ class Ndt5Client(MurakamiRunner): """Run NDT5 test.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="ndt5", description="The Network Diagnostic Tool v5 test.", @@ -24,22 +30,17 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) def _start_test(self): logger.info("Starting NDT5 test...") if shutil.which("ndt5-client") is not None: - cmdargs = [ - "ndt5-client", - "-protocol=ndt5", - "-format=json", - "-quiet" - ] + cmdargs = ["ndt5-client", "-protocol=ndt5", "-format=json", "-quiet"] if "host" in self._config: - logger.info("host value set:"+self._config['host']) - cmdargs.append('-server='+self._config['host']) + logger.info("host value set:" + self._config["host"]) + cmdargs.append("-server=" + self._config["host"]) starttime = datetime.datetime.utcnow() output = subprocess.run( @@ -50,13 +51,13 @@ def _start_test(self): endtime = datetime.datetime.utcnow() murakami_output = { - 'TestName': "ndt5", - 'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'MurakamiLocation': self._location, - 'MurakamiConnectionType': self._connection_type, - 'MurakamiNetworkType': self._network_type, - 'MurakamiDeviceID': self._device_id, + "TestName": "ndt5", + "TestStartTime": starttime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "TestEndTime": endtime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "MurakamiLocation": self._location, + "MurakamiConnectionType": self._connection_type, + "MurakamiNetworkType": self._network_type, + "MurakamiDeviceID": self._device_id, } if output.returncode == 0: @@ -66,54 +67,55 @@ def _start_test(self): summary = json.loads(output.stdout) except json.JSONDecodeError: raise RunnerError( - 'ndt5-client', - 'ndt5-client did not return a valid JSON summary.') + "ndt5-client", + "ndt5-client did not return a valid JSON summary.", + ) logger.info("ndt5 test completed successfully.") # Parse ndt7-client-go's summary JSON and generate Murakami's # output format. - download = summary.get('Download') - upload = summary.get('Upload') - retrans = summary.get('DownloadRetrans') - min_rtt = summary.get('MinRTT') + download = summary.get("Download") + upload = summary.get("Upload") + retrans = summary.get("DownloadRetrans") + min_rtt = summary.get("MinRTT") - murakami_output['ServerName'] = summary.get('ServerFQDN') - murakami_output['ServerIP'] = summary.get('ServerIP') - murakami_output['ClientIP'] = summary.get('ClientIP') - murakami_output['DownloadUUID'] = summary.get('DownloadUUID') + murakami_output["ServerName"] = summary.get("ServerFQDN") + murakami_output["ServerIP"] = summary.get("ServerIP") + murakami_output["ClientIP"] = summary.get("ClientIP") + murakami_output["DownloadUUID"] = summary.get("DownloadUUID") if download is not None: - murakami_output['DownloadValue'] = download.get('Value') - murakami_output['DownloadUnit'] = download.get('Unit') + murakami_output["DownloadValue"] = download.get("Value") + murakami_output["DownloadUnit"] = download.get("Unit") if upload is not None: - murakami_output['UploadValue'] = upload.get('Value') - murakami_output['UploadUnit'] = upload.get('Unit') + murakami_output["UploadValue"] = upload.get("Value") + murakami_output["UploadUnit"] = upload.get("Unit") if retrans is not None: - murakami_output['DownloadRetransValue'] = retrans.get('Value') - murakami_output['DownloadRetransUnit'] = retrans.get('Unit') + murakami_output["DownloadRetransValue"] = retrans.get("Value") + murakami_output["DownloadRetransUnit"] = retrans.get("Unit") if min_rtt is not None: - murakami_output['MinRTTValue'] = min_rtt.get('Value') - murakami_output['MinRTTUnit'] = min_rtt.get('Unit') + murakami_output["MinRTTValue"] = min_rtt.get("Value") + murakami_output["MinRTTUnit"] = min_rtt.get("Unit") else: logger.warn("ndt5 test completed with errors.") # Consider any output as 'TestError'. - murakami_output['TestError'] = output.stdout + murakami_output["TestError"] = output.stdout # All the other fields are set to None (which will become null # in the JSON.) - murakami_output['ServerName'] = None - murakami_output['ServerIP'] = None - murakami_output['ClientIP'] = None - murakami_output['DownloadUUID'] = None - murakami_output['DownloadValue'] = None - murakami_output['DownloadUnit'] = None - murakami_output['UploadValue'] = None - murakami_output['UploadUnit'] = None - murakami_output['DownloadRetransValue'] = None - murakami_output['DownloadRetransUnit'] = None - murakami_output['MinRTTValue'] = None - murakami_output['MinRTTUnit'] = None + murakami_output["ServerName"] = None + murakami_output["ServerIP"] = None + murakami_output["ClientIP"] = None + murakami_output["DownloadUUID"] = None + murakami_output["DownloadValue"] = None + murakami_output["DownloadUnit"] = None + murakami_output["UploadValue"] = None + murakami_output["UploadUnit"] = None + murakami_output["DownloadRetransValue"] = None + murakami_output["DownloadRetransUnit"] = None + murakami_output["MinRTTValue"] = None + murakami_output["MinRTTUnit"] = None return json.dumps(murakami_output) else: raise RunnerError( diff --git a/murakami/runners/ndt5custom.py b/murakami/runners/ndt5custom.py index 50ae44c..cb9842e 100644 --- a/murakami/runners/ndt5custom.py +++ b/murakami/runners/ndt5custom.py @@ -1,10 +1,9 @@ +import datetime +import json import logging import shutil import subprocess -import uuid -import datetime -import json -import pkg_resources +from importlib.metadata import entry_points from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -14,9 +13,16 @@ class Ndt5ClientCustom(MurakamiRunner): """Run NDT5 test.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="ndt5custom", description="The Network Diagnostic Tool v5 test.", @@ -25,15 +31,14 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) self._server_selection = {} # Load all the available server selection algorithms. - for entry_point in pkg_resources.iter_entry_points( - "murakami.selection" - ): + selection_eps = entry_points(group="murakami.selection") + for entry_point in selection_eps: self._server_selection[entry_point.name] = entry_point.load()() def _run_client(self, args): @@ -46,13 +51,13 @@ def _run_client(self, args): endtime = datetime.datetime.utcnow() murakami_output = { - 'TestName': "ndt5", - 'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'MurakamiLocation': self._location, - 'MurakamiConnectionType': self._connection_type, - 'MurakamiNetworkType': self._network_type, - 'MurakamiDeviceID': self._device_id, + "TestName": "ndt5", + "TestStartTime": starttime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "TestEndTime": endtime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "MurakamiLocation": self._location, + "MurakamiConnectionType": self._connection_type, + "MurakamiNetworkType": self._network_type, + "MurakamiDeviceID": self._device_id, } if output.returncode == 0: @@ -62,97 +67,99 @@ def _run_client(self, args): summary = json.loads(output.stdout) except json.JSONDecodeError: raise RunnerError( - 'ndt5-client', - 'ndt5-client did not return a valid JSON summary.') + "ndt5-client", "ndt5-client did not return a valid JSON summary." + ) logger.info("ndt5 test completed successfully.") # Parse ndt7-client-go's summary JSON and generate Murakami's # output format. - download = summary.get('Download') - upload = summary.get('Upload') - retrans = summary.get('DownloadRetrans') - min_rtt = summary.get('MinRTT') - - murakami_output['ServerName'] = summary.get('ServerFQDN') - murakami_output['ServerIP'] = summary.get('ServerIP') - murakami_output['ClientIP'] = summary.get('ClientIP') - murakami_output['DownloadUUID'] = summary.get('DownloadUUID') + download = summary.get("Download") + upload = summary.get("Upload") + retrans = summary.get("DownloadRetrans") + min_rtt = summary.get("MinRTT") + + murakami_output["ServerName"] = summary.get("ServerFQDN") + murakami_output["ServerIP"] = summary.get("ServerIP") + murakami_output["ClientIP"] = summary.get("ClientIP") + murakami_output["DownloadUUID"] = summary.get("DownloadUUID") if download is not None: - murakami_output['DownloadValue'] = download.get('Value') - murakami_output['DownloadUnit'] = download.get('Unit') + murakami_output["DownloadValue"] = download.get("Value") + murakami_output["DownloadUnit"] = download.get("Unit") if upload is not None: - murakami_output['UploadValue'] = upload.get('Value') - murakami_output['UploadUnit'] = upload.get('Unit') + murakami_output["UploadValue"] = upload.get("Value") + murakami_output["UploadUnit"] = upload.get("Unit") if retrans is not None: - murakami_output['DownloadRetransValue'] = retrans.get('Value') - murakami_output['DownloadRetransUnit'] = retrans.get('Unit') + murakami_output["DownloadRetransValue"] = retrans.get("Value") + murakami_output["DownloadRetransUnit"] = retrans.get("Unit") if min_rtt is not None: - murakami_output['MinRTTValue'] = min_rtt.get('Value') - murakami_output['MinRTTUnit'] = min_rtt.get('Unit') + murakami_output["MinRTTValue"] = min_rtt.get("Value") + murakami_output["MinRTTUnit"] = min_rtt.get("Unit") else: logger.warn("ndt5 test completed with errors.") # Consider any output as 'TestError'. - murakami_output['TestError'] = output.stdout + murakami_output["TestError"] = output.stdout # All the other fields are set to None (which will become null # in the JSON.) - murakami_output['ServerName'] = None - murakami_output['ServerIP'] = None - murakami_output['ClientIP'] = None - murakami_output['DownloadUUID'] = None - murakami_output['DownloadValue'] = None - murakami_output['DownloadUnit'] = None - murakami_output['UploadValue'] = None - murakami_output['UploadUnit'] = None - murakami_output['DownloadRetransValue'] = None - murakami_output['DownloadRetransUnit'] = None - murakami_output['MinRTTValue'] = None - murakami_output['MinRTTUnit'] = None - + murakami_output["ServerName"] = None + murakami_output["ServerIP"] = None + murakami_output["ClientIP"] = None + murakami_output["DownloadUUID"] = None + murakami_output["DownloadValue"] = None + murakami_output["DownloadUnit"] = None + murakami_output["UploadValue"] = None + murakami_output["UploadUnit"] = None + murakami_output["DownloadRetransValue"] = None + murakami_output["DownloadRetransUnit"] = None + murakami_output["MinRTTValue"] = None + murakami_output["MinRTTUnit"] = None + return json.dumps(murakami_output) def _start_test(self): logger.info("Starting ndt5custom test...") - + # Check that a configuration file has been specified if "config" not in self._config: raise RunnerError( - 'ndt5custom', - 'No configuration file specified for the custom runner, \ - skipping.') - + "ndt5custom", + "No configuration file specified for the custom runner, \ + skipping.", + ) + # Check that the ndt5-client executable is available. - if shutil.which('ndt5-client') is None: + if shutil.which("ndt5-client") is None: raise RunnerError( - 'ndt5custom', + "ndt5custom", "Executable ndt5-client does not exist, please install ndt5-client-go.", ) custom_config = {} try: - with open(self._config['config']) as config_file: + with open(self._config["config"]) as config_file: custom_config = json.load(config_file) - except IOError as err: + except OSError as err: raise RunnerError( - 'ndt5custom', - 'Cannot open the custom configuration file: ' + str(err)) - + "ndt5custom", "Cannot open the custom configuration file: " + str(err) + ) + # Get all the servers to run measurements against from the config, # applying the corresponding selection algorithm. servers = set() - for group in custom_config.get('serverGroups', []): + for group in custom_config.get("serverGroups", []): # the default selection algorithm is 'random' - selection = group.get('selection', 'random') + selection = group.get("selection", "random") if selection not in self._server_selection: raise RunnerError( - 'ndt5custom', - 'Invalid server selection algorithm specified:' + - selection) + "ndt5custom", + "Invalid server selection algorithm specified:" + selection, + ) servers = servers | self._server_selection[selection].get_servers( - group.get('servers', [])) + group.get("servers", []) + ) # Run a measurement against each of the selected servers. results = [] @@ -162,7 +169,7 @@ def _start_test(self): "-protocol=ndt5", "-format=json", "-quiet", - "-server=" + server + "-server=" + server, ] logger.info("Running ndt5custom measurement (server): " + server) results.append(self._run_client(cmdargs)) diff --git a/murakami/runners/ndt7.py b/murakami/runners/ndt7.py index 91d9d40..4105d67 100644 --- a/murakami/runners/ndt7.py +++ b/murakami/runners/ndt7.py @@ -1,9 +1,8 @@ +import datetime +import json import logging import shutil import subprocess -import uuid -import json -import datetime from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -13,9 +12,16 @@ class Ndt7Client(MurakamiRunner): """Run ndt7 test.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="ndt7", description="The Network Diagnostic Tool v7 test.", @@ -24,24 +30,19 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) def _start_test(self): logger.info("Starting ndt7 test...") if shutil.which("ndt7-client") is not None: - cmdargs = [ - "ndt7-client", - "-format=json", - "-quiet", - "-scheme=ws" - ] + cmdargs = ["ndt7-client", "-format=json", "-quiet", "-scheme=ws"] if "host" in self._config: - cmdargs.append("-server=" + self._config['host']) - insecure = self._config.get('insecure', True) + cmdargs.append("-server=" + self._config["host"]) + insecure = self._config.get("insecure", True) if insecure: - cmdargs.append('-no-verify') + cmdargs.append("-no-verify") starttime = datetime.datetime.utcnow() output = subprocess.run( @@ -52,13 +53,13 @@ def _start_test(self): endtime = datetime.datetime.utcnow() murakami_output = { - 'TestName': "ndt7", - 'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'MurakamiLocation': self._location, - 'MurakamiConnectionType': self._connection_type, - 'MurakamiNetworkType': self._network_type, - 'MurakamiDeviceID': self._device_id, + "TestName": "ndt7", + "TestStartTime": starttime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "TestEndTime": endtime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "MurakamiLocation": self._location, + "MurakamiConnectionType": self._connection_type, + "MurakamiNetworkType": self._network_type, + "MurakamiDeviceID": self._device_id, } if output.returncode == 0: @@ -68,40 +69,44 @@ def _start_test(self): summary = json.loads(output.stdout) except json.JSONDecodeError: raise RunnerError( - 'ndt7-client', - 'ndt7-client did not return a valid JSON summary.' + "ndt7-client", + "ndt7-client did not return a valid JSON summary.", ) logger.info("ndt7 test completed successfully.") # Parse ndt7-client-go's summary JSON and generate Murakami's # output format. - download = summary.get('Download') - upload = summary.get('Upload') + download = summary.get("Download") + upload = summary.get("Upload") - murakami_output['ServerName'] = summary.get('ServerFQDN') - murakami_output['ServerIP'] = summary.get('ServerIP') - murakami_output['ClientIP'] = summary.get('ClientIP') - murakami_output['DownloadUUID'] = download.get('UUID') + murakami_output["ServerName"] = summary.get("ServerFQDN") + murakami_output["ServerIP"] = summary.get("ServerIP") + murakami_output["ClientIP"] = summary.get("ClientIP") + murakami_output["DownloadUUID"] = download.get("UUID") if download is not None: throughput = download.get("Throughput") if throughput is not None: - murakami_output['DownloadValue'] = throughput.get('Value') - murakami_output['DownloadUnit'] = throughput.get('Unit') - murakami_output['DownloadError'] = None + murakami_output["DownloadValue"] = throughput.get("Value") + murakami_output["DownloadUnit"] = throughput.get("Unit") + murakami_output["DownloadError"] = None retransmission = download.get("Retransmission") if retransmission is not None: - murakami_output['DownloadRetransValue'] = retransmission.get('Value') - murakami_output['DownloadRetransUnit'] = retransmission.get('Unit') + murakami_output["DownloadRetransValue"] = retransmission.get( + "Value" + ) + murakami_output["DownloadRetransUnit"] = retransmission.get( + "Unit" + ) latency = download.get("Latency") if latency is not None: - murakami_output['MinRTTValue'] = latency.get('Value') - murakami_output['MinRTTUnit'] = latency.get('Unit') + murakami_output["MinRTTValue"] = latency.get("Value") + murakami_output["MinRTTUnit"] = latency.get("Unit") if upload is not None: throughput = upload.get("Throughput") if throughput is not None: - murakami_output['UploadValue'] = throughput.get('Value') - murakami_output['UploadUnit'] = throughput.get('Unit') - murakami_output['UploadError'] = None + murakami_output["UploadValue"] = throughput.get("Value") + murakami_output["UploadUnit"] = throughput.get("Unit") + murakami_output["UploadError"] = None else: logger.warn("ndt7 test completed with errors.") @@ -111,31 +116,29 @@ def _start_test(self): for j in errors: try: message = json.loads(j) - if message['Value']['Test'] == 'upload': - murakami_output['UploadError'] = ( - message['Value']['Failure'] - ) - elif message['Value']['Test'] == 'download': - murakami_output['DownloadError']= ( - message['Value']['Failure'] - ) + if message["Value"]["Test"] == "upload": + murakami_output["UploadError"] = message["Value"]["Failure"] + elif message["Value"]["Test"] == "download": + murakami_output["DownloadError"] = message["Value"][ + "Failure" + ] except Exception as exc: logger.error("Cannot parse error message: %s", exc) # All the other fields are set to None (which will become null # in the JSON.) - murakami_output['ServerName'] = None - murakami_output['ServerIP'] = None - murakami_output['ClientIP'] = None - murakami_output['DownloadUUID'] = None - murakami_output['DownloadValue'] = None - murakami_output['DownloadUnit'] = None - murakami_output['UploadValue'] = None - murakami_output['UploadUnit'] = None - murakami_output['DownloadRetransValue'] = None - murakami_output['DownloadRetransUnit'] = None - murakami_output['RTTValue'] = None - murakami_output['RTTUnit'] = None + murakami_output["ServerName"] = None + murakami_output["ServerIP"] = None + murakami_output["ClientIP"] = None + murakami_output["DownloadUUID"] = None + murakami_output["DownloadValue"] = None + murakami_output["DownloadUnit"] = None + murakami_output["UploadValue"] = None + murakami_output["UploadUnit"] = None + murakami_output["DownloadRetransValue"] = None + murakami_output["DownloadRetransUnit"] = None + murakami_output["RTTValue"] = None + murakami_output["RTTUnit"] = None return json.dumps(murakami_output) else: raise RunnerError( diff --git a/murakami/runners/ndt7custom.py b/murakami/runners/ndt7custom.py index 79bfe20..16a89a6 100644 --- a/murakami/runners/ndt7custom.py +++ b/murakami/runners/ndt7custom.py @@ -1,10 +1,9 @@ +import datetime +import json import logging import shutil import subprocess -import uuid -import json -import datetime -import pkg_resources +from importlib.metadata import entry_points from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -14,9 +13,16 @@ class Ndt7ClientCustom(MurakamiRunner): """Run ndt7 test.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="ndt7custom", description="The Network Diagnostic Tool v7 test.", @@ -25,15 +31,14 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) self._server_selection = {} # Load all the available server selection algorithms. - for entry_point in pkg_resources.iter_entry_points( - "murakami.selection" - ): + selection_eps = entry_points(group="murakami.selection") + for entry_point in selection_eps: self._server_selection[entry_point.name] = entry_point.load()() def _run_client(self, args): @@ -46,13 +51,13 @@ def _run_client(self, args): endtime = datetime.datetime.utcnow() murakami_output = { - 'TestName': "ndt7", - 'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'MurakamiLocation': self._location, - 'MurakamiConnectionType': self._connection_type, - 'MurakamiNetworkType': self._network_type, - 'MurakamiDeviceID': self._device_id, + "TestName": "ndt7", + "TestStartTime": starttime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "TestEndTime": endtime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "MurakamiLocation": self._location, + "MurakamiConnectionType": self._connection_type, + "MurakamiNetworkType": self._network_type, + "MurakamiDeviceID": self._device_id, } if output.returncode == 0: @@ -62,40 +67,41 @@ def _run_client(self, args): summary = json.loads(output.stdout) except json.JSONDecodeError: raise RunnerError( - 'ndt7-client', - 'ndt7-client did not return a valid JSON summary.' + "ndt7-client", "ndt7-client did not return a valid JSON summary." ) logger.info("ndt7 test completed successfully.") # Parse ndt7-client-go's summary JSON and generate Murakami's # output format. - download = summary.get('Download') - upload = summary.get('Upload') + download = summary.get("Download") + upload = summary.get("Upload") - murakami_output['ServerName'] = summary.get('ServerFQDN') - murakami_output['ServerIP'] = summary.get('ServerIP') - murakami_output['ClientIP'] = summary.get('ClientIP') - murakami_output['DownloadUUID'] = download.get('UUID') + murakami_output["ServerName"] = summary.get("ServerFQDN") + murakami_output["ServerIP"] = summary.get("ServerIP") + murakami_output["ClientIP"] = summary.get("ClientIP") + murakami_output["DownloadUUID"] = download.get("UUID") if download is not None: throughput = download.get("Throughput") if throughput is not None: - murakami_output['DownloadValue'] = throughput.get('Value') - murakami_output['DownloadUnit'] = throughput.get('Unit') - murakami_output['DownloadError'] = None + murakami_output["DownloadValue"] = throughput.get("Value") + murakami_output["DownloadUnit"] = throughput.get("Unit") + murakami_output["DownloadError"] = None retransmission = download.get("Retransmission") if retransmission is not None: - murakami_output['DownloadRetransValue'] = retransmission.get('Value') - murakami_output['DownloadRetransUnit'] = retransmission.get('Unit') + murakami_output["DownloadRetransValue"] = retransmission.get( + "Value" + ) + murakami_output["DownloadRetransUnit"] = retransmission.get("Unit") latency = download.get("Latency") if latency is not None: - murakami_output['MinRTTValue'] = latency.get('Value') - murakami_output['MinRTTUnit'] = latency.get('Unit') + murakami_output["MinRTTValue"] = latency.get("Value") + murakami_output["MinRTTUnit"] = latency.get("Unit") if upload is not None: throughput = download.get("Throughput") if throughput is not None: - murakami_output['UploadValue'] = throughput.get('Value') - murakami_output['UploadUnit'] = throughput.get('Unit') - murakami_output['UploadError'] = None + murakami_output["UploadValue"] = throughput.get("Value") + murakami_output["UploadUnit"] = throughput.get("Unit") + murakami_output["UploadError"] = None else: logger.warn("ndt7 test completed with errors.") @@ -105,32 +111,28 @@ def _run_client(self, args): for j in errors: try: message = json.loads(j) - if message['Value']['Test'] == 'upload': - murakami_output['UploadError'] = ( - message['Value']['Failure'] - ) - elif message['Value']['Test'] == 'download': - murakami_output['DownloadError']= ( - message['Value']['Failure'] - ) + if message["Value"]["Test"] == "upload": + murakami_output["UploadError"] = message["Value"]["Failure"] + elif message["Value"]["Test"] == "download": + murakami_output["DownloadError"] = message["Value"]["Failure"] except Exception as exc: logger.error("Cannot parse error message: %s", exc) # All the other fields are set to None (which will become null # in the JSON.) - murakami_output['ServerName'] = None - murakami_output['ServerIP'] = None - murakami_output['ClientIP'] = None - murakami_output['DownloadUUID'] = None - murakami_output['DownloadValue'] = None - murakami_output['DownloadUnit'] = None - murakami_output['UploadValue'] = None - murakami_output['UploadUnit'] = None - murakami_output['DownloadRetransValue'] = None - murakami_output['DownloadRetransUnit'] = None - murakami_output['RTTValue'] = None - murakami_output['RTTUnit'] = None - + murakami_output["ServerName"] = None + murakami_output["ServerIP"] = None + murakami_output["ClientIP"] = None + murakami_output["DownloadUUID"] = None + murakami_output["DownloadValue"] = None + murakami_output["DownloadUnit"] = None + murakami_output["UploadValue"] = None + murakami_output["UploadUnit"] = None + murakami_output["DownloadRetransValue"] = None + murakami_output["DownloadRetransUnit"] = None + murakami_output["RTTValue"] = None + murakami_output["RTTUnit"] = None + return json.dumps(murakami_output) def _start_test(self): @@ -139,40 +141,42 @@ def _start_test(self): # Check that a configuration file has been specified if "config" not in self._config: raise RunnerError( - 'ndt7custom', - 'No configuration file specified for the custom runner, \ - skipping.') - + "ndt7custom", + "No configuration file specified for the custom runner, \ + skipping.", + ) + # Check that the ndt7-client executable is available. - if shutil.which('ndt7-client') is None: + if shutil.which("ndt7-client") is None: raise RunnerError( - 'ndt7custom', + "ndt7custom", "Executable ndt7-client does not exist, please install ndt7-client-go.", ) custom_config = {} try: - with open(self._config['config']) as config_file: + with open(self._config["config"]) as config_file: custom_config = json.load(config_file) - except IOError as err: + except OSError as err: raise RunnerError( - 'ndt7custom', - 'Cannot open the custom configuration file: ' + str(err)) - + "ndt7custom", "Cannot open the custom configuration file: " + str(err) + ) + # Get all the servers to run measurements against from the config, # applying the corresponding selection algorithm. servers = set() - for group in custom_config.get('serverGroups', []): + for group in custom_config.get("serverGroups", []): # the default selection algorithm is 'random' - selection = group.get('selection', 'random') + selection = group.get("selection", "random") if selection not in self._server_selection: raise RunnerError( - 'ndt7custom', - 'Invalid server selection algorithm specified:' + - selection) + "ndt7custom", + "Invalid server selection algorithm specified:" + selection, + ) servers = servers | self._server_selection[selection].get_servers( - group.get('servers', [])) + group.get("servers", []) + ) # Run a measurement against each of the selected servers. results = [] @@ -182,41 +186,42 @@ def _start_test(self): "-format=json", "-quiet", "-scheme=ws", - "-server=" + server + "-server=" + server, ] - - insecure = self._config.get('insecure', True) + + insecure = self._config.get("insecure", True) if insecure: - cmdargs.append('-no-verify') - + cmdargs.append("-no-verify") + logger.info("Running ndt7custom measurement (server): " + server) results.append(self._run_client(cmdargs)) - + # Check for additional countries/regions specified and run the client # using the locate service for each of them. - countries = custom_config.get('countries', []) + countries = custom_config.get("countries", []) for country in countries: cmdargs = [ "ndt7-client", "-format=json", "-quiet", "-scheme=ws", - "-locate.url=https://locate.measurementlab.net/v2/nearest/?country=" + country + "-locate.url=https://locate.measurementlab.net/v2/nearest/?country=" + + country, ] logger.info("Running ndt7custom measurement (country): " + country) results.append(self._run_client(cmdargs)) - - regions = custom_config.get('regions', []) + + regions = custom_config.get("regions", []) for region in regions: cmdargs = [ "ndt7-client", "-format=json", "-quiet", "-scheme=ws", - "-locate.url=https://locate.measurementlab.net/v2/nearest/?region=" + region + "-locate.url=https://locate.measurementlab.net/v2/nearest/?region=" + + region, ] logger.info("Running ndt7custom measurement (region): " + region) results.append(self._run_client(cmdargs)) return results - diff --git a/murakami/runners/ooniprobe.py b/murakami/runners/ooniprobe.py index 5cb3b7b..3a21d69 100644 --- a/murakami/runners/ooniprobe.py +++ b/murakami/runners/ooniprobe.py @@ -1,8 +1,8 @@ +import datetime +import json import logging import shutil import subprocess -import json -import datetime from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -12,9 +12,16 @@ class OONIProbeClient(MurakamiRunner): """Run ooniprobe's unattended tests (websites, instant messaging, etc.).""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="ooniprobe", description="Test the blocking of websites and apps.", @@ -23,14 +30,14 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) def _start_test(self): logger.info("Starting ooniprobe test...") if shutil.which("ooniprobe") is not None: output = None - + # Empty the ooniprobe database. # We do that to avoid wasting disk space with ooniprobe's database (which we don't use). logger.info("Emptying ooniprobe database...") @@ -44,7 +51,7 @@ def _start_test(self): cmdargs, check=True, ) - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError: raise RunnerError( "ooniprobe reset --force returned a non-zero exit code." ) @@ -58,16 +65,14 @@ def _start_test(self): ] try: - output = subprocess.run( - cmdargs, - check=True, - stderr=subprocess.PIPE - ) - except subprocess.CalledProcessError as e: + output = subprocess.run(cmdargs, check=True, stderr=subprocess.PIPE) + except subprocess.CalledProcessError: raise RunnerError( - "ooniprobe onboard --yes returned a non-zero exit code. (err: " + output.stderr + ")" + "ooniprobe onboard --yes returned a non-zero exit code. (err: " + + output.stderr + + ")" ) - + # Run "ooniprobe run unattended" to start the tests. logger.info("Running ooniprobe tests...") starttime = datetime.datetime.utcnow() @@ -75,43 +80,36 @@ def _start_test(self): "ooniprobe", "--software-name=murakami-ooniprobe", "run", - "unattended" + "unattended", ] try: - output = subprocess.run( - cmdargs, - check=True, - stderr=subprocess.PIPE - ) - except subprocess.CalledProcessError as e: + output = subprocess.run(cmdargs, check=True, stderr=subprocess.PIPE) + except subprocess.CalledProcessError: raise RunnerError( - "ooniprobe run unattended returned a non-zero exit code. (err: " + output.stderr + ")" + "ooniprobe run unattended returned a non-zero exit code. (err: " + + output.stderr + + ")" ) endtime = datetime.datetime.utcnow() - + # If the previous commands succeeded, then we can parse the output # of "ooniprobe list --batch", which only contains the results of # the current run (since the database was emptied beforehand). logging.info("Reading ooniprobe results...") - cmdargs = [ - "ooniprobe", - "list", - "--batch" - ] + cmdargs = ["ooniprobe", "list", "--batch"] try: output = subprocess.run( - cmdargs, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE + cmdargs, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError: raise RunnerError( - "ooniprobe list --batch returned a non-zero exit code. (err: " + output.stderr + ")" + "ooniprobe list --batch returned a non-zero exit code. (err: " + + output.stderr + + ")" ) - + # Parse the output of "ooniprobe list --batch". The output is in # JSONL format, and we need to only parse lines with type result_item. results = [] @@ -119,7 +117,9 @@ def _start_test(self): results = [json.loads(line) for line in output.stdout.splitlines()] except json.decoder.JSONDecodeError as e: raise RunnerError( - "ooniprobe list --batch returned invalid JSON. (err: " + str(e) + ")" + "ooniprobe list --batch returned invalid JSON. (err: " + + str(e) + + ")" ) test_results = [] @@ -131,35 +131,32 @@ def _start_test(self): # Get the test's name. test_name = js["fields"]["name"] - cmdargs = [ - "ooniprobe", - "list", - str(js["fields"]["id"]), - "--batch" - ] + cmdargs = ["ooniprobe", "list", str(js["fields"]["id"]), "--batch"] try: output = subprocess.run( cmdargs, check=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, ) - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError: raise RunnerError( - "ooni probe list --batch returned a non-zero exit code. (err: " + output.stderr + ")" + "ooni probe list --batch returned a non-zero exit code. (err: " + + output.stderr + + ")" ) try: # Wrap the test results in a JSON that's compatible with Murakami. murakami_output = { - 'TestName': "ooniprobe-" + test_name, - 'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'), - 'MurakamiLocation': self._location, - 'MurakamiConnectionType': self._connection_type, - 'MurakamiNetworkType': self._network_type, - 'MurakamiDeviceID': self._device_id, + "TestName": "ooniprobe-" + test_name, + "TestStartTime": starttime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "TestEndTime": endtime.strftime("%Y-%m-%dT%H:%M:%S.%f"), + "MurakamiLocation": self._location, + "MurakamiConnectionType": self._connection_type, + "MurakamiNetworkType": self._network_type, + "MurakamiDeviceID": self._device_id, } test_measurements = [] for line in output.stdout.splitlines(): @@ -170,7 +167,9 @@ def _start_test(self): test_results.append(json.dumps(murakami_output)) except json.decoder.JSONDecodeError as e: raise RunnerError( - "ooni probe list --batch returned invalid JSON. (err: " + str(e) + ")" + "ooni probe list --batch returned invalid JSON. (err: " + + str(e) + + ")" ) - + return test_results diff --git a/murakami/runners/speedtest.py b/murakami/runners/speedtest.py index 673f944..7851679 100644 --- a/murakami/runners/speedtest.py +++ b/murakami/runners/speedtest.py @@ -1,10 +1,8 @@ +import datetime +import json import logging import shutil import subprocess -import uuid -import datetime -import json -import codecs from murakami.errors import RunnerError from murakami.runner import MurakamiRunner @@ -14,9 +12,16 @@ class SpeedtestClient(MurakamiRunner): """Run Speedtest.net tests.""" - def __init__(self, config=None, data_cb=None, - location=None, network_type=None, connection_type=None, - device_id=None): + + def __init__( + self, + config=None, + data_cb=None, + location=None, + network_type=None, + connection_type=None, + device_id=None, + ): super().__init__( title="Ookla-Speedtest", description="The Ookla/Speedtest.net CLI client (https://www.speedtest.net/apps/cli).", @@ -25,84 +30,91 @@ def __init__(self, config=None, data_cb=None, location=location, network_type=network_type, connection_type=connection_type, - device_id=device_id + device_id=device_id, ) - + def _start_test(self): logger.info("Starting Ookla Speedtest...") if shutil.which("speedtest") is not None: - starttime = datetime.datetime.utcnow() - output = subprocess.check_output(["speedtest", "--format=json"],stdin=None, stderr=subprocess.PIPE, shell=False, universal_newlines=False) - + output = subprocess.check_output( + ["speedtest", "--format=json"], + stdin=None, + stderr=subprocess.PIPE, + shell=False, + universal_newlines=False, + ) + endtime = datetime.datetime.utcnow() murakami_output = {} - murakami_output['TestName'] = "ookla-speedtest" - murakami_output['TestStartTime'] = starttime.strftime('%Y-%m-%dT%H:%M:%S.%f') - murakami_output['TestEndTime'] = endtime.strftime('%Y-%m-%dT%H:%M:%S.%f') - murakami_output['MurakamiLocation'] = self._location - murakami_output['MurakamiConnectionType'] = self._connection_type - murakami_output['MurakamiNetworkType'] = self._network_type - murakami_output['MurakamiDeviceID'] = self._device_id + murakami_output["TestName"] = "ookla-speedtest" + murakami_output["TestStartTime"] = starttime.strftime( + "%Y-%m-%dT%H:%M:%S.%f" + ) + murakami_output["TestEndTime"] = endtime.strftime("%Y-%m-%dT%H:%M:%S.%f") + murakami_output["MurakamiLocation"] = self._location + murakami_output["MurakamiConnectionType"] = self._connection_type + murakami_output["MurakamiNetworkType"] = self._network_type + murakami_output["MurakamiDeviceID"] = self._device_id + + output_json = output.decode("utf8") - output_json = output.decode('utf8') - summary = {} summary = json.loads(output_json) - ping = summary.get('ping') - download = summary.get('download') - download_latency = download.get('latency') - upload = summary.get('upload') - upload_latency = upload.get('latency') - interface = summary.get('interface') - server = summary.get('server') - result = summary.get('result') + ping = summary.get("ping") + download = summary.get("download") + download_latency = download.get("latency") + upload = summary.get("upload") + upload_latency = upload.get("latency") + interface = summary.get("interface") + server = summary.get("server") + result = summary.get("result") - murakami_output['Type'] = summary.get('type') - murakami_output['Timestamp'] = summary.get('timestamp') - murakami_output['PingJitter'] = ping.get('jitter') - murakami_output['PingLatency'] = ping.get('latency') - murakami_output['PingLow'] = ping.get('low') - murakami_output['PingHigh'] = ping.get('high') - murakami_output['DownloadBandwidth'] = download.get('bandwidth') - murakami_output['DownloadUnit'] = 'Bytes/s' - murakami_output['DownloadBytes'] = download.get('bytes') - murakami_output['DownloadElapsed'] = download.get('elapsed') - murakami_output['DownloadLatencyIqm'] = download_latency.get('iqm') - murakami_output['DownloadLatencyLow'] = download_latency.get('low') - murakami_output['DownloadLatencyHigh'] = download_latency.get('high') - murakami_output['DownloadLatencyJitter'] = download_latency.get('jitter') - murakami_output['UploadBandwidth'] = upload.get('bandwidth') - murakami_output['UploadUnit'] = 'Bytes/s' - murakami_output['UploadBytes'] = upload.get('bytes') - murakami_output['UploadElapsed'] = upload.get('elapsed') - murakami_output['UploadLatencyIqm'] = upload_latency.get('iqm') - murakami_output['UploadLatencyLow'] = upload_latency.get('low') - murakami_output['UploadLatencyHigh'] = upload_latency.get('high') - murakami_output['UploadLatencyJitter'] = upload_latency.get('jitter') - murakami_output['PacketLoss'] = summary.get('packetLoss') - murakami_output['Isp'] = summary.get('isp') - murakami_output['InterfaceInternalIp'] = interface.get('internalIp') - murakami_output['InterfaceName'] = interface.get('name') - murakami_output['InterfaceMacAddr'] = interface.get('macAddr') - murakami_output['InterfaceIsVpn'] = interface.get('isVpn') - murakami_output['InterfaceExternalIp'] = interface.get('externalIp') - murakami_output['ServerId'] = server.get('id') - murakami_output['ServerHost'] = server.get('host') - murakami_output['ServerPort'] = server.get('port') - murakami_output['ServerName'] = server.get('name') - murakami_output['ServerLocation'] = server.get('location') - murakami_output['ServerCountry'] = server.get('country') - murakami_output['ServerIp'] = server.get('ip') - murakami_output['ResultId'] = result.get('id') - murakami_output['ResultUrl'] = result.get('url') - murakami_output['ResultPersisted'] = result.get('persisted') + murakami_output["Type"] = summary.get("type") + murakami_output["Timestamp"] = summary.get("timestamp") + murakami_output["PingJitter"] = ping.get("jitter") + murakami_output["PingLatency"] = ping.get("latency") + murakami_output["PingLow"] = ping.get("low") + murakami_output["PingHigh"] = ping.get("high") + murakami_output["DownloadBandwidth"] = download.get("bandwidth") + murakami_output["DownloadUnit"] = "Bytes/s" + murakami_output["DownloadBytes"] = download.get("bytes") + murakami_output["DownloadElapsed"] = download.get("elapsed") + murakami_output["DownloadLatencyIqm"] = download_latency.get("iqm") + murakami_output["DownloadLatencyLow"] = download_latency.get("low") + murakami_output["DownloadLatencyHigh"] = download_latency.get("high") + murakami_output["DownloadLatencyJitter"] = download_latency.get("jitter") + murakami_output["UploadBandwidth"] = upload.get("bandwidth") + murakami_output["UploadUnit"] = "Bytes/s" + murakami_output["UploadBytes"] = upload.get("bytes") + murakami_output["UploadElapsed"] = upload.get("elapsed") + murakami_output["UploadLatencyIqm"] = upload_latency.get("iqm") + murakami_output["UploadLatencyLow"] = upload_latency.get("low") + murakami_output["UploadLatencyHigh"] = upload_latency.get("high") + murakami_output["UploadLatencyJitter"] = upload_latency.get("jitter") + murakami_output["PacketLoss"] = summary.get("packetLoss") + murakami_output["Isp"] = summary.get("isp") + murakami_output["InterfaceInternalIp"] = interface.get("internalIp") + murakami_output["InterfaceName"] = interface.get("name") + murakami_output["InterfaceMacAddr"] = interface.get("macAddr") + murakami_output["InterfaceIsVpn"] = interface.get("isVpn") + murakami_output["InterfaceExternalIp"] = interface.get("externalIp") + murakami_output["ServerId"] = server.get("id") + murakami_output["ServerHost"] = server.get("host") + murakami_output["ServerPort"] = server.get("port") + murakami_output["ServerName"] = server.get("name") + murakami_output["ServerLocation"] = server.get("location") + murakami_output["ServerCountry"] = server.get("country") + murakami_output["ServerIp"] = server.get("ip") + murakami_output["ResultId"] = result.get("id") + murakami_output["ResultUrl"] = result.get("url") + murakami_output["ResultPersisted"] = result.get("persisted") return json.dumps(murakami_output) else: raise RunnerError( - "speedtest", - "Executable does not exist, please install speedtest.") + "speedtest", "Executable does not exist, please install speedtest." + ) diff --git a/murakami/selection/all.py b/murakami/selection/all.py index bc24458..08c97bd 100644 --- a/murakami/selection/all.py +++ b/murakami/selection/all.py @@ -3,4 +3,4 @@ def __init__(self): pass def get_servers(self, server_group): - return set(server_group) \ No newline at end of file + return set(server_group) diff --git a/murakami/selection/rand.py b/murakami/selection/rand.py index acab513..f7ae939 100644 --- a/murakami/selection/rand.py +++ b/murakami/selection/rand.py @@ -1,8 +1,9 @@ import random + class RandomSelection: def __init__(self): pass def get_servers(self, server_group): - return set([random.choice(server_group)]) \ No newline at end of file + return set([random.choice(server_group)]) diff --git a/murakami/server.py b/murakami/server.py index 3f91eff..ee83c8e 100644 --- a/murakami/server.py +++ b/murakami/server.py @@ -6,17 +6,17 @@ import datetime import logging import random -import pkg_resources +from importlib.metadata import entry_points from apscheduler.schedulers.tornado import TornadoScheduler from apscheduler.triggers.base import BaseTrigger -from tornado.ioloop import IOLoop from tornado import gen -from webthing import WebThingServer, SingleThing +from tornado.ioloop import IOLoop +from webthing import SingleThing, WebThingServer import murakami.defaults as defaults -from murakami.thing import MurakamiThing import murakami.utils as utils +from murakami.thing import MurakamiThing _logger = logging.getLogger(__name__) @@ -28,15 +28,15 @@ class RandomTrigger(BaseTrigger): An implementation of apscheduler's BaseTrigger to schedule tests in an exponential distribution. """ + def __init__(self, *args, **kwargs): - self._tests_per_day = kwargs.pop("tests_per_day", - defaults.TESTS_PER_DAY) + self._tests_per_day = kwargs.pop("tests_per_day", defaults.TESTS_PER_DAY) self._immediate = kwargs.pop("immediate", False) def get_next_fire_time(self, previous_fire_time, now): sleeptime = random.expovariate( - 1.0 / - (datetime.timedelta(days=1).total_seconds() / self._tests_per_day)) + 1.0 / (datetime.timedelta(days=1).total_seconds() / self._tests_per_day) + ) if not previous_fire_time: _logger.debug("Not previously fired before") if self._immediate: @@ -65,21 +65,22 @@ class MurakamiServer: * `connection_type`: string describing type of connection this device is using """ + def __init__( - self, - port=defaults.HTTP_PORT, - hostname=None, - ssl_options=None, - additional_routes=None, - base_path="", - tests_per_day=defaults.TESTS_PER_DAY, - immediate=False, - webthings=False, - location=None, - network_type=None, - connection_type=None, - device_id=None, - config=None, + self, + port=defaults.HTTP_PORT, + hostname=None, + ssl_options=None, + additional_routes=None, + base_path="", + tests_per_day=defaults.TESTS_PER_DAY, + immediate=False, + webthings=False, + location=None, + network_type=None, + connection_type=None, + device_id=None, + config=None, ): self._runners = {} self._exporters = {} @@ -115,15 +116,16 @@ def _call_exporters(self, test_name="", data="", timestamp=None): try: e.push(test_name, data, timestamp) except Exception as exc: - _logger.error("Failed to run exporter %s: %s", e.name, - str(exc)) + _logger.error("Failed to run exporter %s: %s", e.name, str(exc)) def _load_runners(self): - trigger = RandomTrigger(tests_per_day=self._tests_per_day, - immediate=self._immediate) + trigger = RandomTrigger( + tests_per_day=self._tests_per_day, immediate=self._immediate + ) # Load test runners - for entry_point in pkg_resources.iter_entry_points("murakami.runners"): + eps = entry_points(group="murakami.runners") + for entry_point in eps: logging.debug("Loading test runner %s", entry_point.name) if "tests" not in self._config: self._config["tests"] = {} @@ -152,17 +154,16 @@ def _load_runners(self): # Start test scheduler if enabled if self._tests_per_day > 0: self._scheduler = TornadoScheduler() - self._scheduler.add_job(self._call_runners, - id="runners", - name="Test Runners", - trigger=trigger) + self._scheduler.add_job( + self._call_runners, id="runners", name="Test Runners", trigger=trigger + ) def _load_exporters(self): self._exporters = {} # Check if exporters are enabled and load them. if "exporters" in self._config: - exporters = pkg_resources.get_entry_map("murakami", - group="murakami.exporters") + exporter_eps = entry_points(group="murakami.exporters") + exporters = {ep.name: ep for ep in exporter_eps} for name, entry in self._config["exporters"].items(): logging.debug("Loading exporter %s", name) enabled = True @@ -171,14 +172,13 @@ def _load_exporters(self): if enabled: if "type" in entry: if entry["type"] in exporters: - self._exporters[name] = exporters[ - entry["type"]].load()( - name=name, - location=self._location, - network_type=self._network_type, - connection_type=self._connection_type, - config=entry, - ) + self._exporters[name] = exporters[entry["type"]].load()( + name=name, + location=self._location, + network_type=self._network_type, + connection_type=self._connection_type, + config=entry, + ) else: logging.error( "No available exporter type %s, skipping.", @@ -186,7 +186,8 @@ def _load_exporters(self): ) else: logging.error( - "No type defined for exporter %s, skipping.", name) + "No type defined for exporter %s, skipping.", name + ) else: logging.debug("Exporter %s disabled, skipping.", name) diff --git a/murakami/thing.py b/murakami/thing.py index 6689723..401bdd3 100644 --- a/murakami/thing.py +++ b/murakami/thing.py @@ -1,6 +1,7 @@ """ This module contains the Thing wrapper, used for accessing tests via WebThings. """ + from webthing import Property, Thing, Value @@ -12,6 +13,7 @@ class MurakamiThing(Thing): ####Arguments * `runners`: a List of MurakamiRunner instances passed from MurakamiServer """ + def __init__(self, runners): super().__init__( id_="https://github.com/throneless-tech/murakami", @@ -33,4 +35,5 @@ def __init__(self, runners): "type": "boolean", "description": runner.description, }, - )) + ) + ) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 5f2a0bf..0000000 --- a/poetry.lock +++ /dev/null @@ -1,2061 +0,0 @@ -[[package]] -name = "apscheduler" -version = "3.8.1" -description = "In-process task scheduler with Cron-like capabilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.dependencies] -pytz = "*" -six = ">=1.4.0" -tzlocal = ">=2.0,<3.0.0 || >=4.0.0" - -[package.extras] -asyncio = ["trollius"] -doc = ["sphinx", "sphinx-rtd-theme"] -gevent = ["gevent"] -mongodb = ["pymongo (>=3.0)"] -redis = ["redis (>=3.0)"] -rethinkdb = ["rethinkdb (>=2.4.0)"] -sqlalchemy = ["sqlalchemy (>=0.8)"] -testing = ["pytest (<6)", "pytest-cov", "pytest-tornado5", "mock", "pytest-asyncio (<0.6)", "pytest-asyncio"] -tornado = ["tornado (>=4.3)"] -twisted = ["twisted"] -zookeeper = ["kazoo"] - -[[package]] -name = "astroid" -version = "2.8.4" -description = "An abstract syntax tree for Python with inference support." -category = "dev" -optional = false -python-versions = "~=3.6" - -[package.dependencies] -lazy-object-proxy = ">=1.4.0" -typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} -wrapt = ">=1.11,<1.14" - -[[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "attrs" -version = "21.2.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] - -[[package]] -name = "backports.entry-points-selectable" -version = "1.1.0" -description = "Compatibility shim providing selectable entry points for older implementations" -category = "dev" -optional = false -python-versions = ">=2.7" - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"] - -[[package]] -name = "backports.zoneinfo" -version = "0.2.1" -description = "Backport of the standard library zoneinfo module" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-resources = {version = "*", markers = "python_version < \"3.7\""} - -[package.extras] -tzdata = ["tzdata"] - -[[package]] -name = "bcrypt" -version = "3.2.0" -description = "Modern password hashing for your software and your servers" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.1" -six = ">=1.4.1" - -[package.extras] -tests = ["pytest (>=3.2.1,!=3.3.0)"] -typecheck = ["mypy"] - -[[package]] -name = "cachetools" -version = "4.2.4" -description = "Extensible memoizing collections and decorators" -category = "main" -optional = false -python-versions = "~=3.5" - -[[package]] -name = "certifi" -version = "2021.10.8" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "cffi" -version = "1.15.0" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "2.0.7" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "click" -version = "8.0.3" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "configargparse" -version = "0.14.0" -description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." -category = "main" -optional = false -python-versions = "*" - -[package.extras] -yaml = ["pyyaml"] - -[[package]] -name = "coverage" -version = "6.0.2" -description = "Code coverage measurement for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "cryptography" -version = "35.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] -pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools_rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] - -[[package]] -name = "dataclasses" -version = "0.7" -description = "A backport of the dataclasses module for Python 3.6" -category = "dev" -optional = false -python-versions = ">=3.6, <3.7" - -[[package]] -name = "distlib" -version = "0.3.3" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "docstring-parser" -version = "0.7.3" -description = "" -category = "dev" -optional = false -python-versions = "~=3.5" - -[[package]] -name = "falcon" -version = "2.0.0" -description = "An unladen web framework for building APIs and app backends." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "filelock" -version = "3.3.1" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] - -[[package]] -name = "ghp-import" -version = "2.0.2" -description = "Copy your docs directly to the gh-pages branch." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["twine", "markdown", "flake8", "wheel"] - -[[package]] -name = "gitdb" -version = "4.0.9" -description = "Git Object Database" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.20" -description = "Python Git Library" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} - -[[package]] -name = "google-api-core" -version = "2.2.0" -description = "Google API client core library" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -google-auth = ">=1.25.0,<3.0dev" -googleapis-common-protos = ">=1.52.0,<2.0dev" -protobuf = ">=3.12.0" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] - -[[package]] -name = "google-auth" -version = "2.3.2" -description = "Google Authentication Library" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" - -[package.dependencies] -cachetools = ">=2.0.0,<5.0" -pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} -six = ">=1.9.0" - -[package.extras] -aiohttp = ["requests (>=2.20.0,<3.0.0dev)", "aiohttp (>=3.6.2,<4.0.0dev)"] -pyopenssl = ["pyopenssl (>=20.0.0)"] -reauth = ["pyu2f (>=0.1.5)"] - -[[package]] -name = "google-cloud-core" -version = "2.1.0" -description = "Google Cloud API client core library" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -google-api-core = ">=1.21.0,<3.0.0dev" -google-auth = ">=1.24.0,<3.0dev" - -[package.extras] -grpc = ["grpcio (>=1.8.2,<2.0dev)"] - -[[package]] -name = "google-cloud-storage" -version = "1.42.3" -description = "Google Cloud Storage API client library" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" - -[package.dependencies] -google-api-core = {version = ">=1.29.0,<3.0dev", markers = "python_version >= \"3.6\""} -google-auth = {version = ">=1.25.0,<3.0dev", markers = "python_version >= \"3.6\""} -google-cloud-core = {version = ">=1.6.0,<3.0dev", markers = "python_version >= \"3.6\""} -google-resumable-media = {version = ">=1.3.0,<3.0dev", markers = "python_version >= \"3.6\""} -protobuf = {version = "*", markers = "python_version >= \"3.6\""} -requests = ">=2.18.0,<3.0.0dev" -six = "*" - -[[package]] -name = "google-crc32c" -version = "1.3.0" -description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -testing = ["pytest"] - -[[package]] -name = "google-resumable-media" -version = "2.1.0" -description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" -optional = false -python-versions = ">= 3.6" - -[package.dependencies] -google-crc32c = ">=1.0,<2.0dev" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] -requests = ["requests (>=2.18.0,<3.0.0dev)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.53.0" -description = "Common protobufs used in Google APIs" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -protobuf = ">=3.12.0" - -[package.extras] -grpc = ["grpcio (>=1.0.0)"] - -[[package]] -name = "hug" -version = "2.6.1" -description = "A Python framework that makes developing APIs as simple as possible, but no simpler." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -falcon = "2.0.0" -requests = "*" - -[[package]] -name = "idna" -version = "3.3" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "ifaddr" -version = "0.1.7" -description = "Cross-platform network interface and IP address enumeration library" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "importlib-metadata" -version = "4.8.1" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -perf = ["ipython"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] - -[[package]] -name = "importlib-resources" -version = "5.3.0" -description = "Read resources from Python packages" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] - -[[package]] -name = "isort" -version = "5.8.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] - -[[package]] -name = "jinja2" -version = "3.0.2" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonlines" -version = "1.2.0" -description = "Library with helpers for the jsonlines file format" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - -[[package]] -name = "jsonschema" -version = "4.0.0" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "lazy-object-proxy" -version = "1.6.0" -description = "A fast and thorough lazy object proxy." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[[package]] -name = "livejson" -version = "1.9.1" -description = "Bind Python objects to JSON files" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "mako" -version = "1.1.5" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["babel"] -lingua = ["lingua"] - -[[package]] -name = "markdown" -version = "3.3.4" -description = "Python implementation of Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markupsafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mergedeep" -version = "1.3.4" -description = "A deep merge function for 🐍." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mkdocs" -version = "1.2.3" -description = "Project documentation with Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -click = ">=3.3" -ghp-import = ">=1.0" -importlib-metadata = ">=3.10" -Jinja2 = ">=2.10.1" -Markdown = ">=3.2.1" -mergedeep = ">=1.3.4" -packaging = ">=20.5" -PyYAML = ">=3.10" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] - -[[package]] -name = "mkdocs-material" -version = "7.3.0" -description = "A Material Design theme for MkDocs" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -markdown = ">=3.2" -mkdocs = ">=1.2.2" -mkdocs-material-extensions = ">=1.0" -Pygments = ">=2.4" -pymdown-extensions = ">=7.0" - -[[package]] -name = "mkdocs-material-extensions" -version = "1.0.3" -description = "Extension pack for Python Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "more-itertools" -version = "8.10.0" -description = "More routines for operating on iterables, beyond itertools" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "packaging" -version = "21.0" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2" - -[[package]] -name = "paramiko" -version = "2.8.0" -description = "SSH2 protocol library" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -bcrypt = ">=3.1.3" -cryptography = ">=2.5" -pynacl = ">=1.0.1" - -[package.extras] -all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] -ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"] -gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] -invoke = ["invoke (>=1.3)"] - -[[package]] -name = "pdocs" -version = "1.1.1" -description = "A simple program and library to auto generate API documentation for Python modules." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -dataclasses = {version = ">=0.7,<0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} -docstring_parser = ">=0.7.2,<0.8.0" -hug = ">=2.6,<3.0" -Mako = ">=1.1,<2.0" -Markdown = ">=3.0.0,<4.0.0" - -[[package]] -name = "platformdirs" -version = "2.4.0" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "portray" -version = "1.7.0" -description = "Your Project with Great Documentation" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -GitPython = ">=3.0,<4.0" -hug = ">=2.6,<3.0" -livereload = ">=2.6.3,<3.0.0" -mkdocs = ">=1.2,<1.3" -mkdocs-material = ">=7.0,<8.0" -pdocs = ">=1.1.1,<2.0.0" -pymdown-extensions = ">=7.0,<8.0" -toml = ">=0.10.0,<0.11.0" -yaspin = ">=0.15.0,<0.16.0" - -[[package]] -name = "protobuf" -version = "3.19.0" -description = "Protocol Buffers" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "py" -version = "1.10.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pyasn1" -version = "0.4.8" -description = "ASN.1 types and codecs" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pyasn1-modules" -version = "0.2.8" -description = "A collection of ASN.1-based protocols modules." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pyasn1 = ">=0.4.6,<0.5.0" - -[[package]] -name = "pycparser" -version = "2.20" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pyee" -version = "8.2.2" -description = "A port of node.js's EventEmitter to python." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pygments" -version = "2.10.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "pylint" -version = "2.11.1" -description = "python code static checker" -category = "dev" -optional = false -python-versions = "~=3.6" - -[package.dependencies] -astroid = ">=2.8.0,<2.9" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.7" -platformdirs = ">=2.2.0" -toml = ">=0.7.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[[package]] -name = "pymdown-extensions" -version = "7.1" -description = "Extension pack for Python Markdown." -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" - -[package.dependencies] -Markdown = ">=3.2" - -[[package]] -name = "pynacl" -version = "1.4.0" -description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -cffi = ">=1.4.1" -six = "*" - -[package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] - -[[package]] -name = "pyparsing" -version = "3.0.3" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.18.0" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pytest" -version = "3.10.1" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -atomicwrites = ">=1.0" -attrs = ">=17.4.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -more-itertools = ">=4.0.0" -pluggy = ">=0.7" -py = ">=1.5.0" -six = ">=1.10.0" - -[[package]] -name = "pytest-cov" -version = "2.9.0" -description = "Pytest plugin for measuring coverage." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -coverage = ">=4.4" -pytest = ">=3.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2021.3" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version >= \"3.6\" and python_version < \"3.9\""} -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyyaml = "*" - -[[package]] -name = "requests" -version = "2.26.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "rsa" -version = "4.7.2" -description = "Pure-Python RSA implementation" -category = "main" -optional = false -python-versions = ">=3.5, <4" - -[package.dependencies] -pyasn1 = ">=0.1.3" - -[[package]] -name = "scp" -version = "0.13.6" -description = "scp module for paramiko" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -paramiko = "*" - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "smmap" -version = "5.0.0" -description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomlkit" -version = "0.5.11" -description = "Style preserving TOML library" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "tornado" -version = "6.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "main" -optional = false -python-versions = ">= 3.5" - -[[package]] -name = "tox" -version = "3.24.4" -description = "tox is a generic virtualenv management and test command line tool" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -packaging = ">=14" -pluggy = ">=0.12.0" -py = ">=1.4.17" -six = ">=1.14.0" -toml = ">=0.9.4" -virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" - -[package.extras] -docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] - -[[package]] -name = "typed-ast" -version = "1.4.3" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "typing-extensions" -version = "3.10.0.2" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "tzdata" -version = "2021.5" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" - -[[package]] -name = "tzlocal" -version = "4.0.2" -description = "tzinfo object for the local timezone" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} -pytz-deprecation-shim = "*" -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] -test = ["pytest-mock (>=3.3)", "pytest (>=4.3)"] - -[[package]] -name = "urllib3" -version = "1.26.7" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "virtualenv" -version = "20.9.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -"backports.entry-points-selectable" = ">=1.0.4" -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -platformdirs = ">=2,<3" -six = ">=1.9.0,<2" - -[package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] - -[[package]] -name = "watchdog" -version = "2.1.6" -description = "Filesystem events monitoring" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "webthing" -version = "0.12.2" -description = "HTTP Web Thing implementation" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" - -[package.dependencies] -ifaddr = ">=0.1.0" -jsonschema = ">=3.2.0" -pyee = ">=7.0.0" -tornado = ">=6.0.0" -zeroconf = ">=0.21.0" - -[[package]] -name = "wrapt" -version = "1.13.2" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "yaspin" -version = "0.15.0" -description = "Yet Another Terminal Spinner" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "zeroconf" -version = "0.36.9" -description = "Pure Python Multicast DNS Service Discovery Library (Bonjour/Avahi compatible)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -ifaddr = ">=0.1.7" - -[[package]] -name = "zipp" -version = "3.6.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.6" -content-hash = "e9ee8a995ce249ce78cc00ca935b7e4e94f0ddeaa743e852bed13f997cc3b13b" - -[metadata.files] -apscheduler = [ - {file = "APScheduler-3.8.1-py2.py3-none-any.whl", hash = "sha256:c22cb14b411a31435eb2c530dfbbec948ac63015b517087c7978adb61b574865"}, - {file = "APScheduler-3.8.1.tar.gz", hash = "sha256:5cf344ebcfbdaa48ae178c029c055cec7bc7a4a47c21e315e4d1f08bd35f2355"}, -] -astroid = [ - {file = "astroid-2.8.4-py3-none-any.whl", hash = "sha256:0755c998e7117078dcb7d0bda621391dd2a85da48052d948c7411ab187325346"}, - {file = "astroid-2.8.4.tar.gz", hash = "sha256:1e83a69fd51b013ebf5912d26b9338d6643a55fec2f20c787792680610eed4a2"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, - {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, -] -"backports.entry-points-selectable" = [ - {file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"}, - {file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"}, -] -"backports.zoneinfo" = [ - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, - {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, -] -bcrypt = [ - {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"}, - {file = "bcrypt-3.2.0-cp36-abi3-win32.whl", hash = "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55"}, - {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"}, - {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"}, -] -cachetools = [ - {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, - {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"}, -] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -cffi = [ - {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, - {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, - {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, - {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, - {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, - {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, - {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, - {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, - {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, - {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, - {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, - {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, - {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, - {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, - {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, - {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, - {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, - {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, -] -click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -configargparse = [ - {file = "ConfigArgParse-0.14.0.tar.gz", hash = "sha256:2e2efe2be3f90577aca9415e32cb629aa2ecd92078adbe27b53a03e53ff12e91"}, -] -coverage = [ - {file = "coverage-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0"}, - {file = "coverage-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa"}, - {file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7"}, - {file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd"}, - {file = "coverage-6.0.2-cp310-cp310-win32.whl", hash = "sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7"}, - {file = "coverage-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d"}, - {file = "coverage-6.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3"}, - {file = "coverage-6.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a"}, - {file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9"}, - {file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2"}, - {file = "coverage-6.0.2-cp36-cp36m-win32.whl", hash = "sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122"}, - {file = "coverage-6.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9"}, - {file = "coverage-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4"}, - {file = "coverage-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7"}, - {file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc"}, - {file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1"}, - {file = "coverage-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330"}, - {file = "coverage-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1"}, - {file = "coverage-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff"}, - {file = "coverage-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d"}, - {file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc"}, - {file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb"}, - {file = "coverage-6.0.2-cp38-cp38-win32.whl", hash = "sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f"}, - {file = "coverage-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9"}, - {file = "coverage-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24"}, - {file = "coverage-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822"}, - {file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0"}, - {file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe"}, - {file = "coverage-6.0.2-cp39-cp39-win32.whl", hash = "sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce"}, - {file = "coverage-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9"}, - {file = "coverage-6.0.2-pp36-none-any.whl", hash = "sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164"}, - {file = "coverage-6.0.2-pp37-none-any.whl", hash = "sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895"}, - {file = "coverage-6.0.2.tar.gz", hash = "sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149"}, -] -cryptography = [ - {file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"}, - {file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"}, - {file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"}, - {file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"}, - {file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"}, -] -dataclasses = [ - {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, - {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, -] -distlib = [ - {file = "distlib-0.3.3-py2.py3-none-any.whl", hash = "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31"}, - {file = "distlib-0.3.3.zip", hash = "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"}, -] -docstring-parser = [ - {file = "docstring_parser-0.7.3.tar.gz", hash = "sha256:cde5fbf8b846433dfbde1e0f96b7f909336a634d5df34a38cb75050c7346734a"}, -] -falcon = [ - {file = "falcon-2.0.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:733033ec80c896e30a43ab3e776856096836787197a44eb21022320a61311983"}, - {file = "falcon-2.0.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f93351459f110b4c1ee28556aef9a791832df6f910bea7b3f616109d534df06b"}, - {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e9efa0791b5d9f9dd9689015ea6bce0a27fcd5ecbcd30e6d940bffa4f7f03389"}, - {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:59d1e8c993b9a37ea06df9d72cf907a46cc8063b30717cdac2f34d1658b6f936"}, - {file = "falcon-2.0.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:a5ebb22a04c9cc65081938ee7651b4e3b4d2a28522ea8ec04c7bdd2b3e9e8cd8"}, - {file = "falcon-2.0.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:95bf6ce986c1119aef12c9b348f4dee9c6dcc58391bdd0bc2b0bf353c2b15986"}, - {file = "falcon-2.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:aa184895d1ad4573fbfaaf803563d02f019ebdf4790e41cc568a330607eae439"}, - {file = "falcon-2.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:74cf1d18207381c665b9e6292d65100ce146d958707793174b03869dc6e614f4"}, - {file = "falcon-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24adcd2b29a8ffa9d552dc79638cd21736a3fb04eda7d102c6cebafdaadb88ad"}, - {file = "falcon-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:18157af2a4fc3feedf2b5dcc6196f448639acf01c68bc33d4d5a04c3ef87f494"}, - {file = "falcon-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e3782b7b92fefd46a6ad1fd8fe63fe6c6f1b7740a95ca56957f48d1aee34b357"}, - {file = "falcon-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9712975adcf8c6e12876239085ad757b8fdeba223d46d23daef82b47658f83a9"}, - {file = "falcon-2.0.0-py2.py3-none-any.whl", hash = "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53"}, - {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, -] -filelock = [ - {file = "filelock-3.3.1-py3-none-any.whl", hash = "sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f"}, - {file = "filelock-3.3.1.tar.gz", hash = "sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f"}, -] -ghp-import = [ - {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, - {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, -] -gitdb = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, -] -gitpython = [ - {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, - {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, -] -google-api-core = [ - {file = "google-api-core-2.2.0.tar.gz", hash = "sha256:dca67b5e369a5127ec00c8ea02de48517e39c1aad882e2e6d85da8050520cab6"}, - {file = "google_api_core-2.2.0-py2.py3-none-any.whl", hash = "sha256:8d20c8919fa32bfc9e2f67f98def477218102aae6b70e4912818aeb53fd4c256"}, -] -google-auth = [ - {file = "google-auth-2.3.2.tar.gz", hash = "sha256:2dc5218ee1192f9d67147cece18f47a929a9ef746cb69c50ab5ff5cfc983647b"}, - {file = "google_auth-2.3.2-py2.py3-none-any.whl", hash = "sha256:6e99f4b3b099feb50de20302f2f8987c1c36e80a3f856ce852675bdf7a0935d3"}, -] -google-cloud-core = [ - {file = "google-cloud-core-2.1.0.tar.gz", hash = "sha256:35a1f5f02a86e0fa2e28c669f0db4a76d928671a28fbbbb493ab59ba9d1cb9a9"}, - {file = "google_cloud_core-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d5fed11731dae8bc8656a2c9fa8ff17bdfdfd083cba97569324e35b94e7e002"}, -] -google-cloud-storage = [ - {file = "google-cloud-storage-1.42.3.tar.gz", hash = "sha256:7754d4dcaa45975514b404ece0da2bb4292acbc67ca559a69e12a19d54fcdb06"}, - {file = "google_cloud_storage-1.42.3-py2.py3-none-any.whl", hash = "sha256:71ee3a0dcf2c139f034a054181cd7658f1ec8f12837d2769c450a8a00fcd4c6d"}, -] -google-crc32c = [ - {file = "google-crc32c-1.3.0.tar.gz", hash = "sha256:276de6273eb074a35bc598f8efbc00c7869c5cf2e29c90748fccc8c898c244df"}, - {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cb6994fff247987c66a8a4e550ef374671c2b82e3c0d2115e689d21e511a652d"}, - {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9da0a39b53d2fab3e5467329ed50e951eb91386e9d0d5b12daf593973c3b168"}, - {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:eb0b14523758e37802f27b7f8cd973f5f3d33be7613952c0df904b68c4842f0e"}, - {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95c68a4b9b7828ba0428f8f7e3109c5d476ca44996ed9a5f8aac6269296e2d59"}, - {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c3cf890c3c0ecfe1510a452a165431b5831e24160c5fcf2071f0f85ca5a47cd"}, - {file = "google_crc32c-1.3.0-cp310-cp310-win32.whl", hash = "sha256:3bbce1be3687bbfebe29abdb7631b83e6b25da3f4e1856a1611eb21854b689ea"}, - {file = "google_crc32c-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:c124b8c8779bf2d35d9b721e52d4adb41c9bfbde45e6a3f25f0820caa9aba73f"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:42ae4781333e331a1743445931b08ebdad73e188fd554259e772556fc4937c48"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ff71073ebf0e42258a42a0b34f2c09ec384977e7f6808999102eedd5b49920e3"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fe31de3002e7b08eb20823b3735b97c86c5926dd0581c7710a680b418a8709d4"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7760a88a8d3d705ff562aa93f8445ead54f58fd482e4f9e2bafb7e177375d4"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0b9e622c3b2b8d0ce32f77eba617ab0d6768b82836391e4f8f9e2074582bf02"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-win32.whl", hash = "sha256:779cbf1ce375b96111db98fca913c1f5ec11b1d870e529b1dc7354b2681a8c3a"}, - {file = "google_crc32c-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:04e7c220798a72fd0f08242bc8d7a05986b2a08a0573396187fd32c1dcdd58b3"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e7a539b9be7b9c00f11ef16b55486141bc2cdb0c54762f84e3c6fc091917436d"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ca60076c388728d3b6ac3846842474f4250c91efbfe5afa872d3ffd69dd4b318"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05340b60bf05b574159e9bd940152a47d38af3fb43803ffe71f11d704b7696a6"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:318f73f5484b5671f0c7f5f63741ab020a599504ed81d209b5c7129ee4667407"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f58099ad7affc0754ae42e6d87443299f15d739b0ce03c76f515153a5cda06c"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:f52a4ad2568314ee713715b1e2d79ab55fab11e8b304fd1462ff5cccf4264b3e"}, - {file = "google_crc32c-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bab4aebd525218bab4ee615786c4581952eadc16b1ff031813a2fd51f0cc7b08"}, - {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dda4d8a3bb0b50f540f6ff4b6033f3a74e8bf0bd5320b70fab2c03e512a62812"}, - {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fec221a051150eeddfdfcff162e6db92c65ecf46cb0f7bb1bf812a1520ec026b"}, - {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:226f2f9b8e128a6ca6a9af9b9e8384f7b53a801907425c9a292553a3a7218ce0"}, - {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7f9cbea4245ee36190f85fe1814e2d7b1e5f2186381b082f5d59f99b7f11328"}, - {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a4db36f9721fdf391646685ecffa404eb986cbe007a3289499020daf72e88a2"}, - {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:12674a4c3b56b706153a358eaa1018c4137a5a04635b92b4652440d3d7386206"}, - {file = "google_crc32c-1.3.0-cp38-cp38-win32.whl", hash = "sha256:650e2917660e696041ab3dcd7abac160b4121cd9a484c08406f24c5964099829"}, - {file = "google_crc32c-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:58be56ae0529c664cc04a9c76e68bb92b091e0194d6e3c50bea7e0f266f73713"}, - {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:96a8918a78d5d64e07c8ea4ed2bc44354e3f93f46a4866a40e8db934e4c0d74b"}, - {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:13af315c3a0eec8bb8b8d80b8b128cb3fcd17d7e4edafc39647846345a3f003a"}, - {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6311853aa2bba4064d0c28ca54e7b50c4d48e3de04f6770f6c60ebda1e975267"}, - {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ed447680ff21c14aaceb6a9f99a5f639f583ccfe4ce1a5e1d48eb41c3d6b3217"}, - {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c1d6236feab51200272d79b3d3e0f12cf2cbb12b208c835b175a21efdb0a73"}, - {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e0f1ff55dde0ebcfbef027edc21f71c205845585fffe30d4ec4979416613e9b3"}, - {file = "google_crc32c-1.3.0-cp39-cp39-win32.whl", hash = "sha256:fbd60c6aaa07c31d7754edbc2334aef50601b7f1ada67a96eb1eb57c7c72378f"}, - {file = "google_crc32c-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:127f9cc3ac41b6a859bd9dc4321097b1a4f6aa7fdf71b4f9227b9e3ebffb4422"}, - {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fc28e0db232c62ca0c3600884933178f0825c99be4474cdd645e378a10588125"}, - {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1926fd8de0acb9d15ee757175ce7242e235482a783cd4ec711cc999fc103c24e"}, - {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5da2c81575cc3ccf05d9830f9e8d3c70954819ca9a63828210498c0774fda1a3"}, - {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f712ce54e0d631370e1f4997b3f182f3368179198efc30d477c75d1f44942"}, - {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7f6fe42536d9dcd3e2ffb9d3053f5d05221ae3bbcefbe472bdf2c71c793e3183"}, -] -google-resumable-media = [ - {file = "google-resumable-media-2.1.0.tar.gz", hash = "sha256:725b989e0dd387ef2703d1cc8e86217474217f4549593c477fd94f4024a0f911"}, - {file = "google_resumable_media-2.1.0-py2.py3-none-any.whl", hash = "sha256:cdc75ea0361e39704dc7df7da59fbd419e73c8bc92eac94d8a020d36baa9944b"}, -] -googleapis-common-protos = [ - {file = "googleapis-common-protos-1.53.0.tar.gz", hash = "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4"}, - {file = "googleapis_common_protos-1.53.0-py2.py3-none-any.whl", hash = "sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0"}, -] -hug = [ - {file = "hug-2.6.1-py2.py3-none-any.whl", hash = "sha256:31c8fc284f81377278629a4b94cbb619ae9ce829cdc2da9564ccc66a121046b4"}, - {file = "hug-2.6.1.tar.gz", hash = "sha256:b0edace2acb618873779c9ce6ecf9165db54fef95c22262f5700fcdd9febaec9"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -ifaddr = [ - {file = "ifaddr-0.1.7-py2.py3-none-any.whl", hash = "sha256:d1f603952f0a71c9ab4e705754511e4e03b02565bc4cec7188ad6415ff534cd3"}, - {file = "ifaddr-0.1.7.tar.gz", hash = "sha256:1f9e8a6ca6f16db5a37d3356f07b6e52344f6f9f7e806d618537731669eb1a94"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, - {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, -] -importlib-resources = [ - {file = "importlib_resources-5.3.0-py3-none-any.whl", hash = "sha256:7a65eb0d8ee98eedab76e6deb51195c67f8e575959f6356a6e15fd7e1148f2a3"}, - {file = "importlib_resources-5.3.0.tar.gz", hash = "sha256:f2e58e721b505a79abe67f5868d99f8886aec8594c962c7490d0c22925f518da"}, -] -isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, -] -jinja2 = [ - {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"}, - {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"}, -] -jsonlines = [ - {file = "jsonlines-1.2.0-py2.py3-none-any.whl", hash = "sha256:0ebd5b0c3efe0d4b5018b320fb0ee1a7b680ab39f6eb853715859f818d386cc8"}, - {file = "jsonlines-1.2.0.tar.gz", hash = "sha256:43b8d5588a9d4862c8a4a49580e38e20ec595aee7ad6fe469b10fb83fbefde88"}, -] -jsonschema = [ - {file = "jsonschema-4.0.0-py3-none-any.whl", hash = "sha256:c773028c649441ab980015b5b622f4cd5134cf563daaf0235ca4b73cc3734f20"}, - {file = "jsonschema-4.0.0.tar.gz", hash = "sha256:bc51325b929171791c42ebc1c70b9713eb134d3bb8ebd5474c8b659b15be6d86"}, -] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"}, - {file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"}, - {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"}, - {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"}, - {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"}, - {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"}, - {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"}, - {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"}, - {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"}, - {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"}, - {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"}, - {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"}, - {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"}, - {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"}, - {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"}, - {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"}, - {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"}, - {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"}, - {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"}, - {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"}, - {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, - {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, -] -livejson = [ - {file = "livejson-1.9.1.tar.gz", hash = "sha256:7d08c4a2c607fe0e089f8ba38946e060af6dd617d3d984ec8a3495f29ab370e1"}, -] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -mako = [ - {file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"}, - {file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"}, -] -markdown = [ - {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, - {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, -] -markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mergedeep = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] -mkdocs = [ - {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, - {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, -] -mkdocs-material = [ - {file = "mkdocs-material-7.3.0.tar.gz", hash = "sha256:07db0580fa96c3473aee99ec3fb4606a1a5a1e4f4467e64c0cd1ba8da5b6476e"}, - {file = "mkdocs_material-7.3.0-py2.py3-none-any.whl", hash = "sha256:b183c27dc0f44e631bbc32c51057f61a3e2ba8b3c1080e59f944167eeba9ff1d"}, -] -mkdocs-material-extensions = [ - {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, - {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, -] -more-itertools = [ - {file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"}, - {file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"}, -] -packaging = [ - {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, - {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, -] -paramiko = [ - {file = "paramiko-2.8.0-py2.py3-none-any.whl", hash = "sha256:def3ec612399bab4e9f5eb66b0ae5983980db9dd9120d9e9c6ea3ff673865d1c"}, - {file = "paramiko-2.8.0.tar.gz", hash = "sha256:e673b10ee0f1c80d46182d3af7751d033d9b573dd7054d2d0aa46be186c3c1d2"}, -] -pdocs = [ - {file = "pdocs-1.1.1-py3-none-any.whl", hash = "sha256:4f5116cf5ce0fa9f13171cd74db224636d4d71370115eefce22d8945526fcfc0"}, - {file = "pdocs-1.1.1.tar.gz", hash = "sha256:f148034970220c9e05d2e04d8eb3fcec3575cf480af0966123ef9d6621b46e4f"}, -] -platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -portray = [ - {file = "portray-1.7.0-py3-none-any.whl", hash = "sha256:fb4467105d948fabf0ec35b11af50f3c0c4f2aabaa31d5dcd657fadb1c6132e1"}, - {file = "portray-1.7.0.tar.gz", hash = "sha256:8f3c0a6a6841969329e4dd1e94e180220658c3ad0367a5bad81dd964a75ae1fe"}, -] -protobuf = [ - {file = "protobuf-3.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:01a0645ef3acddfbc90237e1cdfae1086130fc7cb480b5874656193afd657083"}, - {file = "protobuf-3.19.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d3861c9721a90ba83ee0936a9cfcc4fa1c4b4144ac9658fb6f6343b38558e9b4"}, - {file = "protobuf-3.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b64be5d7270cf5e76375bac049846e8a9543a2d4368b69afe78ab725380a7487"}, - {file = "protobuf-3.19.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f6046b9e2feee0dce994493186e8715b4392ed5f50f356280ad9c2f9f93080a"}, - {file = "protobuf-3.19.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac2f8ec942d414609aba0331952ae12bb823e8f424bbb6b8c422f1cef32dc842"}, - {file = "protobuf-3.19.0-cp36-cp36m-win32.whl", hash = "sha256:3fea09aa04ef2f8b01fcc9bb87f19509934f8a35d177c865b8f9ee5c32b60c1b"}, - {file = "protobuf-3.19.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d1f4277d321f60456845ca9b882c4845736f1f5c1c69eb778eba22a97977d8af"}, - {file = "protobuf-3.19.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8488c2276f14f294e890cc1260ab342a13e90cd20dcc03319d2eea258f1fd321"}, - {file = "protobuf-3.19.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:36bf292f44966c67080e535321501717f4f1eba30faef8f2cd4b0c745a027211"}, - {file = "protobuf-3.19.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99af73ae34c93e0e2ace57ea2e70243f34fc015c8c23fd39ee93652e726f7e7"}, - {file = "protobuf-3.19.0-cp37-cp37m-win32.whl", hash = "sha256:f7a031cf8e2fc14acc0ba694f6dff0a01e06b70d817eba6edc72ee6cc20517ac"}, - {file = "protobuf-3.19.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d4ca5f0c7bc8d2e6966ca3bbd85e9ebe7191b6e21f067896d4af6b28ecff29fe"}, - {file = "protobuf-3.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a8a880593015ef2c83f7af797fa4fbf583b2c98b4bd94e46c5b61fee319d84b"}, - {file = "protobuf-3.19.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:6f16925f5c977dd7787973a50c242e60c22b1d1182aba6bec7bd02862579c10f"}, - {file = "protobuf-3.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9097327d277b0aa4a3224e61cd6850aef3269172397715299bcffc9f90293c9"}, - {file = "protobuf-3.19.0-cp38-cp38-win32.whl", hash = "sha256:708d04394a63ee9bdc797938b6e15ed5bf24a1cb37743eb3886fd74a5a67a234"}, - {file = "protobuf-3.19.0-cp38-cp38-win_amd64.whl", hash = "sha256:ee4d07d596357f51316b6ecf1cc1927660e9d5e418385bb1c51fd2496cd9bee7"}, - {file = "protobuf-3.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34a77b8fafdeb8f89fee2b7108ae60d8958d72e33478680cc1e05517892ecc46"}, - {file = "protobuf-3.19.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4f93e0f6af796ddd1502225ff8ea25340ced186ca05b601c44d5c88b45ba80a0"}, - {file = "protobuf-3.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:942dd6bc8bd2a3c6a156d8ab0f80bd45313f22b78e1176283270054dcc8ca4c2"}, - {file = "protobuf-3.19.0-cp39-cp39-win32.whl", hash = "sha256:7b3867795708ac88fde8d6f34f0d9a50af56087e41f624bdb2e9ff808ea5dda7"}, - {file = "protobuf-3.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:a74432e9d28a6072a2359a0f49f81eb14dd718e7dbbfb6c0789b456c49e1f130"}, - {file = "protobuf-3.19.0-py2.py3-none-any.whl", hash = "sha256:c96e94d3e523a82caa3e5f74b35dd1c4884199358d01c950d95c341255ff48bc"}, - {file = "protobuf-3.19.0.tar.gz", hash = "sha256:6a1dc6584d24ef86f5b104bcad64fa0fe06ed36e5687f426e0445d363a041d18"}, -] -py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, -] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pyasn1-modules = [ - {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, - {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"}, - {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"}, - {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"}, - {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"}, - {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, - {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"}, - {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"}, - {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"}, - {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"}, - {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"}, - {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, - {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, -] -pycparser = [ - {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, -] -pyee = [ - {file = "pyee-8.2.2-py2.py3-none-any.whl", hash = "sha256:c09f56e36eb10bf23aa2aacf145f690ded75b990a3d9523fd478b005940303d2"}, - {file = "pyee-8.2.2.tar.gz", hash = "sha256:5c7e60f8df95710dbe17550e16ce0153f83990c00ef744841b43f371ed53ebea"}, -] -pygments = [ - {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, - {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, -] -pylint = [ - {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"}, - {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"}, -] -pymdown-extensions = [ - {file = "pymdown-extensions-7.1.tar.gz", hash = "sha256:5bf93d1ccd8281948cd7c559eb363e59b179b5373478e8a7195cf4b78e3c11b6"}, - {file = "pymdown_extensions-7.1-py2.py3-none-any.whl", hash = "sha256:8f415b21ee86d80bb2c3676f4478b274d0a8ccb13af672a4c86b9ffd22bd005c"}, -] -pynacl = [ - {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, - {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, - {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, - {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, - {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, -] -pyparsing = [ - {file = "pyparsing-3.0.3-py3-none-any.whl", hash = "sha256:f8d3fe9fc404576c5164f0f0c4e382c96b85265e023c409c43d48f65da9d60d0"}, - {file = "pyparsing-3.0.3.tar.gz", hash = "sha256:9e3511118010f112a4b4b435ae50e1eaa610cda191acb9e421d60cf5fde83455"}, -] -pyrsistent = [ - {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"}, - {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, - {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, -] -pytest = [ - {file = "pytest-3.10.1-py2.py3-none-any.whl", hash = "sha256:3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec"}, - {file = "pytest-3.10.1.tar.gz", hash = "sha256:e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"}, -] -pytest-cov = [ - {file = "pytest-cov-2.9.0.tar.gz", hash = "sha256:b6a814b8ed6247bd81ff47f038511b57fe1ce7f4cc25b9106f1a4b106f1d9322"}, - {file = "pytest_cov-2.9.0-py2.py3-none-any.whl", hash = "sha256:c87dfd8465d865655a8213859f1b4749b43448b5fae465cb981e16d52a811424"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pytz = [ - {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, - {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, -] -pytz-deprecation-shim = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -pyyaml-env-tag = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] -requests = [ - {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, - {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, -] -rsa = [ - {file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"}, - {file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"}, -] -scp = [ - {file = "scp-0.13.6-py2.py3-none-any.whl", hash = "sha256:5e23f22b00bdbeed83a982c6b2dfae98c125b80019c15fbb16dd64dfd864a452"}, - {file = "scp-0.13.6.tar.gz", hash = "sha256:0a72f9d782e968b09b114d5607f96b1f16fe9942857afb355399edd55372fcf1"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -smmap = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomlkit = [ - {file = "tomlkit-0.5.11-py2.py3-none-any.whl", hash = "sha256:4e1bd6c9197d984528f9ff0cc9db667c317d8881288db50db20eeeb0f6b0380b"}, - {file = "tomlkit-0.5.11.tar.gz", hash = "sha256:f044eda25647882e5ef22b43a1688fb6ab12af2fc50e8456cdfc751c873101cf"}, -] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, -] -tox = [ - {file = "tox-3.24.4-py2.py3-none-any.whl", hash = "sha256:5e274227a53dc9ef856767c21867377ba395992549f02ce55eb549f9fb9a8d10"}, - {file = "tox-3.24.4.tar.gz", hash = "sha256:c30b57fa2477f1fb7c36aa1d83292d5c2336cd0018119e1b1c17340e2c2708ca"}, -] -typed-ast = [ - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, - {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, - {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, - {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, - {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, - {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, - {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, - {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, - {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, - {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, -] -typing-extensions = [ - {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, - {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, - {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, -] -tzdata = [ - {file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"}, - {file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"}, -] -tzlocal = [ - {file = "tzlocal-4.0.2-py3-none-any.whl", hash = "sha256:9d0bb4c2640616f4965bb229eaf53a9e4c4c69cec3e6ab08eb2a55712e2fe1d3"}, - {file = "tzlocal-4.0.2.tar.gz", hash = "sha256:ab6cf47469cc78a3cf5687d206424512b13ac692162595f024892b39fd9d4e85"}, -] -urllib3 = [ - {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, - {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, -] -virtualenv = [ - {file = "virtualenv-20.9.0-py2.py3-none-any.whl", hash = "sha256:1d145deec2da86b29026be49c775cc5a9aab434f85f7efef98307fb3965165de"}, - {file = "virtualenv-20.9.0.tar.gz", hash = "sha256:bb55ace18de14593947354e5e6cd1be75fb32c3329651da62e92bf5d0aab7213"}, -] -watchdog = [ - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, - {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, - {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, - {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, - {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, - {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, - {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, - {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, -] -webthing = [ - {file = "webthing-0.12.2-py3-none-any.whl", hash = "sha256:72a689737cb5492b8f5556a440f2d9a0b8759a15a309b978c70f2bf5728f4434"}, - {file = "webthing-0.12.2.tar.gz", hash = "sha256:c92cc589ea1b416dafc03e80737e87ccf632aeeccdc4c92428cc9a6c1bae5e71"}, -] -wrapt = [ - {file = "wrapt-1.13.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1"}, - {file = "wrapt-1.13.2-cp35-cp35m-win32.whl", hash = "sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9"}, - {file = "wrapt-1.13.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52"}, - {file = "wrapt-1.13.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e"}, - {file = "wrapt-1.13.2-cp36-cp36m-win32.whl", hash = "sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7"}, - {file = "wrapt-1.13.2-cp36-cp36m-win_amd64.whl", hash = "sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48"}, - {file = "wrapt-1.13.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70"}, - {file = "wrapt-1.13.2-cp37-cp37m-win32.whl", hash = "sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb"}, - {file = "wrapt-1.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4"}, - {file = "wrapt-1.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90"}, - {file = "wrapt-1.13.2-cp38-cp38-win32.whl", hash = "sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a"}, - {file = "wrapt-1.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528"}, - {file = "wrapt-1.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e"}, - {file = "wrapt-1.13.2-cp39-cp39-win32.whl", hash = "sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072"}, - {file = "wrapt-1.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef"}, - {file = "wrapt-1.13.2.tar.gz", hash = "sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447"}, -] -yaspin = [ - {file = "yaspin-0.15.0-py2.py3-none-any.whl", hash = "sha256:0ee4668936d0053de752c9a4963929faa3a832bd0ba823877d27855592dc80aa"}, - {file = "yaspin-0.15.0.tar.gz", hash = "sha256:5a938bdc7bab353fd8942d0619d56c6b5159a80997dc1c387a479b39e6dc9391"}, -] -zeroconf = [ - {file = "zeroconf-0.36.9-py3-none-any.whl", hash = "sha256:dce72fdeda50e722a0d2ca99956b5f089ad338723b13e58bb8a2d9082e3403f2"}, - {file = "zeroconf-0.36.9.tar.gz", hash = "sha256:023ebc58b765537edcb0395367b732e5271e2659760b1d5c6119aaacaeb7b390"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, -] diff --git a/pyproject.toml b/pyproject.toml index fa667bb..1a69042 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,69 +1,106 @@ -[tool.poetry] +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] name = "murakami" version = "0.1.0" description = "A webthings-compliant network measurement tool from M-Lab." -authors = ["Josh King ", "Rae Gaines "] -license = "Apache-2.0" readme = "README.md" -repository = "https://github.com/m-lab/murakami" +license = { text = "Apache-2.0" } +authors = [ + { name = "Josh King", email = "josh@throneless.tech" }, + { name = "Rae Gaines", email = "rae@throneless.tech" }, +] +requires-python = ">=3.11" +dependencies = [ + "webthing>=0.12.0", + "configargparse>=0.14.0", + "tomlkit>=0.5.5", + "apscheduler>=3.6", + "paramiko>=2.6", + "scp>=0.13.2", + "jsonlines>=1.2", + "livejson>=1.8", + "google-cloud-storage>=1.26.0", + "protobuf>=7.34.0", + "setuptools>=82.0.0", +] -[tool.poetry.scripts] -murakami = 'murakami.__main__:main' -murakami-convert = 'scripts.convert:main' -murakami-upload = 'scripts.upload:main' +[project.optional-dependencies] +dev = [ + "pytest>=8.0", + "pytest-cov>=5.0", + "ruff>=0.8.0", + "pip-audit>=2.7", + "bandit>=1.7", +] -[tool.poetry.dependencies] -python = "^3.6" -webthing = "^0.12.0" -configargparse = "^0.14.0" -tomlkit = "^0.5.5" -apscheduler = "^3.6" -paramiko = "^2.6" -scp = "^0.13.2" -jsonlines = "^1.2" -livejson = "^1.8" -google-cloud-storage = "^1.26.0" +[project.scripts] +murakami = "murakami.__main__:main" +murakami-convert = "scripts.convert:main" +murakami-upload = "scripts.upload:main" -[tool.poetry.dev-dependencies] -pytest = "^3.0" -pylint = "^2.4" -portray = "^1.3" -pdocs = "^1.0" -tox = "^3.14" -pytest-cov = "^2.8" +[project.urls] +Homepage = "https://github.com/m-lab/murakami" +Repository = "https://github.com/m-lab/murakami" -[tool.poetry.plugins."murakami.runners"] -"dash" = "murakami.runners.dash:DashClient" -"ndt5" = "murakami.runners.ndt5:Ndt5Client" -"ndt7" = "murakami.runners.ndt7:Ndt7Client" -"ndt5custom" = "murakami.runners.ndt5custom:Ndt5ClientCustom" -"ndt7custom" = "murakami.runners.ndt7custom:Ndt7ClientCustom" -"speedtest" = "murakami.runners.speedtest:SpeedtestClient" -"ooniprobe" = "murakami.runners.ooniprobe:OONIProbeClient" +[tool.ruff] +target-version = "py311" +line-length = 88 -[tool.poetry.plugins."murakami.exporters"] -"local" = "murakami.exporters.local:LocalExporter" -"scp" = "murakami.exporters.scp:SCPExporter" -"gcs" = "murakami.exporters.gcs:GCSExporter" -"http" = "murakami.exporters.http:HTTPExporter" +[tool.ruff.lint] +select = [ + "E", + "W", + "F", + "I", + "B", + "C4", + "UP", + "N", + "SIM", + "TCH", + "S", + "RUF", +] +ignore = [ + "E501", + "B008", + "S603", + "S607", + "B904", + "N818", + "UP022", + "UP031", + "SIM102", + "SIM117", + "SIM118", + "SIM108", + "S113", + "S507", + "C405", + "S311", + "S101", +] -[tool.poetry.plugins."murakami.selection"] -"all" = "murakami.selection.all:AllSelection" -"random" = "murakami.selection.rand:RandomSelection" +[tool.ruff.lint.isort] +known-first-party = ["murakami"] -[tool.tox] -legacy_tox_ini = """ -[tox] -isolated_build = true -envlist = py36, py37 +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" -[testenv] -whitelist_externals = poetry -passenv = CI TRAVIS TRAVIS_* -commands = - poetry install -v - poetry run pytest --cov=./ tests/ -""" -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +[tool.hatch.build.targets.wheel] +packages = ["murakami"] + +[dependency-groups] +dev = [ + "bandit>=1.9.4", + "pip-audit>=2.10.0", + "pytest>=9.0.2", + "pytest-cov>=7.0.0", + "ruff>=0.15.4", +] diff --git a/scripts/convert.py b/scripts/convert.py index 2de0c22..1d7f3ec 100644 --- a/scripts/convert.py +++ b/scripts/convert.py @@ -4,15 +4,15 @@ this is a utility script designed to convert them to other formats. """ +import csv import difflib import glob +import json import logging import os import configargparse -import csv import jsonlines -import json logger = logging.getLogger(__name__) @@ -33,6 +33,7 @@ def __str__(self): else: return "ConvertException" + def flatten_json(b, delim): """ A simple function for flattening JSON by concatenating keys w/ a delimiter. @@ -97,12 +98,11 @@ def import_dash_legacy(path): record["test_name"] = data["test_name"] record["test_runtime"] = data["test_runtime"] record["test_start_time"] = data["test_start_time"] - record["connect_latency"] = data["test_keys"]["simple"][ - "connect_latency"] - record["median_bitrate"] = data["test_keys"]["simple"][ - "median_bitrate"] + record["connect_latency"] = data["test_keys"]["simple"]["connect_latency"] + record["median_bitrate"] = data["test_keys"]["simple"]["median_bitrate"] record["min_playout_delay"] = data["test_keys"]["simple"][ - "min_playout_delay"] + "min_playout_delay" + ] record["probe_asn"] = data["probe_asn"] record["probe_cc"] = data["probe_cc"] return record @@ -124,57 +124,58 @@ def import_ndt_legacy(path): record["max_rtt"] = data["test_keys"]["advanced"]["max_rtt"] record["min_rtt"] = data["test_keys"]["advanced"]["min_rtt"] record["congestion_limited"] = data["test_keys"]["advanced"][ - "congestion_limited"] - record["packet_loss"] = data["test_keys"]["advanced"][ - "packet_loss"] - record["sender_limited"] = data["test_keys"]["advanced"][ - "sender_limited"] + "congestion_limited" + ] + record["packet_loss"] = data["test_keys"]["advanced"]["packet_loss"] + record["sender_limited"] = data["test_keys"]["advanced"]["sender_limited"] record["receiver_limited"] = data["test_keys"]["advanced"][ - "receiver_limited"] + "receiver_limited" + ] record["probe_asn"] = data["probe_asn"] record["probe_cc"] = data["probe_cc"] return record + def import_ndt5(path): - print("Converting {}...".format(path)) + print(f"Converting {path}...") with open(path) as f: data = json.load(f) # Check this is an NDT5 test summary. - if data.get('TestName') != "ndt5": - raise ConvertException("{}: Invalid ndt5 output file." - .format(path)) + if data.get("TestName") != "ndt5": + raise ConvertException(f"{path}: Invalid ndt5 output file.") # Check this test completed without errors. - if data.get('TestError') is not None: + if data.get("TestError") is not None: raise ConvertException( - "{}: test did not complete successfully, skipping." - .format(path)) + f"{path}: test did not complete successfully, skipping." + ) return data + def import_ndt7(path): - print("Converting {}...".format(path)) + print(f"Converting {path}...") with open(path) as f: data = json.load(f) # Check this is an ndt7 test summary. if data.get("TestName") != "ndt7": - raise ConvertException("{}: Invalid ndt7 output file." - .format(path)) + raise ConvertException(f"{path}: Invalid ndt7 output file.") # Check this test completed without errors. - if data.get('TestError') is not None: + if data.get("TestError") is not None: raise ConvertException( - "{}: test did not complete successfully, skipping." - .format(path)) + f"{path}: test did not complete successfully, skipping." + ) return data + tests = { "speedtest": import_speedtest, "dash_legacy": import_dash_legacy, "ndt_legacy": import_ndt_legacy, "ndt5": import_ndt5, - "ndt7": import_ndt7 + "ndt7": import_ndt7, } @@ -183,16 +184,18 @@ def export_csv(path, data): Export function for CSV-format output files. """ with open(path, "w", newline="") as file: - writer = csv.DictWriter(file, fieldnames=data[0].keys(), quotechar='"', - quoting=csv.QUOTE_NONNUMERIC) + writer = csv.DictWriter( + file, fieldnames=data[0].keys(), quotechar='"', quoting=csv.QUOTE_NONNUMERIC + ) writer.writeheader() return writer.writerows(data) + exporters = {"csv": export_csv} def main(): - """ The main function for the converter script.""" + """The main function for the converter script.""" parser = configargparse.ArgParser( auto_env_var_prefix="murakami_convert_", description="A Murakami test output file format parser.", @@ -233,8 +236,7 @@ def main(): "-p", "--pattern", dest="pattern", - help= - "An input filename pattern containing one or more of %%l (location type), %%n (network type), %%c (connection type), and %%d (datestamp).", + help="An input filename pattern containing one or more of %%l (location type), %%n (network type), %%c (connection type), and %%d (datestamp).", ) parser.add( "-r", @@ -247,8 +249,7 @@ def main(): parser.add( "input", nargs="+", - help= - "The input filename, directory, or pattern containing test results.", + help="The input filename, directory, or pattern containing test results.", ) settings = parser.parse_args() diff --git a/scripts/upload.py b/scripts/upload.py index d38608b..2418d4d 100644 --- a/scripts/upload.py +++ b/scripts/upload.py @@ -1,17 +1,33 @@ -import json -import glob import argparse +import glob import os + from murakami.exporters.http import HTTPExporter + + def main(): print(os.environ) - parser = argparse.ArgumentParser(description='Uploads JSON results via HTTPExporter') - parser.add_argument('-p', '-path', type=str, dest="path", - help='input path', default=os.environ.get('MURAKAMI_EXPORT_PATH')) - parser.add_argument('-u', '-url', type=str, dest="url", - help='URL to send JSON data to', default=os.environ.get('MURAKAMI_EXPORTERS_HTTP0_URL')) + parser = argparse.ArgumentParser( + description="Uploads JSON results via HTTPExporter" + ) + parser.add_argument( + "-p", + "-path", + type=str, + dest="path", + help="input path", + default=os.environ.get("MURAKAMI_EXPORT_PATH"), + ) + parser.add_argument( + "-u", + "-url", + type=str, + dest="url", + help="URL to send JSON data to", + default=os.environ.get("MURAKAMI_EXPORTERS_HTTP0_URL"), + ) args = parser.parse_args() - exporter = HTTPExporter(config={'url': args.url}) + exporter = HTTPExporter(config={"url": args.url}) print("Reading path " + args.path) files = glob.glob(args.path, recursive=True) print("Files: " + str(files)) @@ -19,5 +35,7 @@ def main(): with open(filename) as f: data = f.read() exporter.push(data=data) + + if __name__ == "__main__": main() diff --git a/tests/test_http.py b/tests/test_http.py index 3f33afc..6689148 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -3,27 +3,24 @@ from murakami.exporters.http import HTTPExporter -RESPONSE_SUCCESS_JSON = { - 'status': 'success' -} +RESPONSE_SUCCESS_JSON = {"status": "success"} -RESPONSE_FAILURE_JSON = { - 'error': '', - 'message': '' -} +RESPONSE_FAILURE_JSON = {"error": "", "message": ""} -@patch('requests.post') + +@patch("requests.post") def test_push_response_ok(mock_post): mock_post.return_value = Mock(ok=True) mock_post.return_value.json.return_value = RESPONSE_SUCCESS_JSON - exporter = HTTPExporter("test", config={'url': 'http://testurl'}) + exporter = HTTPExporter("test", config={"url": "http://testurl"}) assert exporter.push("ndt5", '{"TestName": "ndt5"}', time.time()) is True -@patch('requests.post') + +@patch("requests.post") def test_push_response_error(mock_post): mock_post.return_value = Mock(ok=False) mock_post.return_value.json.return_value = RESPONSE_FAILURE_JSON - exporter = HTTPExporter("test", config={'url': 'http://testurl'}) + exporter = HTTPExporter("test", config={"url": "http://testurl"}) assert exporter.push("ndt5", '{"TestName": "ndt5"}', time.time()) is False diff --git a/tests/test_murakami.py b/tests/test_murakami.py index 5f0bf16..5a284f6 100644 --- a/tests/test_murakami.py +++ b/tests/test_murakami.py @@ -2,4 +2,4 @@ def test_version(): - assert __version__ == '0.1.0' + assert __version__ == "0.1.0"